@mmerterden/multi-agent-pipeline 20.7.0 → 20.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (264) hide show
  1. package/CHANGELOG.md +9 -0
  2. package/LICENSE +0 -10
  3. package/docs/facts.json +1 -1
  4. package/manifest.json +266 -267
  5. package/package.json +2 -2
  6. package/pipeline/scripts/_notices.mjs +1 -1
  7. package/pipeline/skills/.skill-manifest.json +68 -68
  8. package/pipeline/skills/shared/README.md +70 -70
  9. package/pipeline/skills/shared/external/alarmkit/SKILL.md +373 -381
  10. package/pipeline/skills/shared/external/alarmkit/evals/evals.json +23 -18
  11. package/pipeline/skills/shared/external/alarmkit/references/alarmkit-patterns.md +328 -378
  12. package/pipeline/skills/shared/external/app-clips/SKILL.md +260 -160
  13. package/pipeline/skills/shared/external/app-clips/evals/evals.json +27 -27
  14. package/pipeline/skills/shared/external/app-clips/references/data-handoff-notifications-location.md +150 -83
  15. package/pipeline/skills/shared/external/app-clips/references/routing-and-experiences.md +135 -83
  16. package/pipeline/skills/shared/external/app-clips/references/size-capabilities-and-promotion.md +143 -85
  17. package/pipeline/skills/shared/external/app-intents/SKILL.md +302 -304
  18. package/pipeline/skills/shared/external/app-intents/evals/evals.json +21 -21
  19. package/pipeline/skills/shared/external/app-intents/references/appintents-advanced.md +594 -894
  20. package/pipeline/skills/shared/external/app-store-optimization/SKILL.md +339 -277
  21. package/pipeline/skills/shared/external/app-store-optimization/evals/evals.json +27 -23
  22. package/pipeline/skills/shared/external/app-store-optimization/references/keyword-research-methodology.md +105 -122
  23. package/pipeline/skills/shared/external/app-store-optimization/references/product-page-variants.md +143 -166
  24. package/pipeline/skills/shared/external/app-store-review/SKILL.md +307 -326
  25. package/pipeline/skills/shared/external/app-store-review/evals/evals.json +21 -21
  26. package/pipeline/skills/shared/external/app-store-review/references/privacy-manifest.md +105 -67
  27. package/pipeline/skills/shared/external/app-store-review/references/review-checklists.md +114 -101
  28. package/pipeline/skills/shared/external/apple-on-device-ai/SKILL.md +333 -360
  29. package/pipeline/skills/shared/external/apple-on-device-ai/evals/evals.json +24 -27
  30. package/pipeline/skills/shared/external/apple-on-device-ai/references/coreml-conversion.md +215 -322
  31. package/pipeline/skills/shared/external/apple-on-device-ai/references/coreml-optimization.md +161 -256
  32. package/pipeline/skills/shared/external/apple-on-device-ai/references/foundation-models.md +277 -387
  33. package/pipeline/skills/shared/external/apple-on-device-ai/references/mlx-swift.md +196 -210
  34. package/pipeline/skills/shared/external/authentication/SKILL.md +265 -381
  35. package/pipeline/skills/shared/external/authentication/evals/evals.json +25 -25
  36. package/pipeline/skills/shared/external/authentication/references/keychain-biometric.md +133 -178
  37. package/pipeline/skills/shared/external/authentication/references/passkeys.md +111 -147
  38. package/pipeline/skills/shared/external/avkit/SKILL.md +267 -364
  39. package/pipeline/skills/shared/external/avkit/evals/evals.json +26 -26
  40. package/pipeline/skills/shared/external/avkit/references/avkit-patterns.md +375 -493
  41. package/pipeline/skills/shared/external/background-processing/SKILL.md +270 -382
  42. package/pipeline/skills/shared/external/background-processing/evals/evals.json +22 -22
  43. package/pipeline/skills/shared/external/background-processing/references/background-task-patterns.md +169 -317
  44. package/pipeline/skills/shared/external/callkit-voip/SKILL.md +290 -371
  45. package/pipeline/skills/shared/external/callkit-voip/evals/evals.json +24 -24
  46. package/pipeline/skills/shared/external/callkit-voip/references/callkit-patterns.md +175 -343
  47. package/pipeline/skills/shared/external/cloudkit-sync/SKILL.md +292 -381
  48. package/pipeline/skills/shared/external/cloudkit-sync/evals/evals.json +33 -30
  49. package/pipeline/skills/shared/external/cloudkit-sync/references/cloudkit-patterns.md +227 -355
  50. package/pipeline/skills/shared/external/contacts-framework/SKILL.md +197 -346
  51. package/pipeline/skills/shared/external/contacts-framework/evals/evals.json +19 -21
  52. package/pipeline/skills/shared/external/contacts-framework/references/contacts-patterns.md +169 -308
  53. package/pipeline/skills/shared/external/core-bluetooth/SKILL.md +226 -376
  54. package/pipeline/skills/shared/external/core-bluetooth/evals/evals.json +25 -22
  55. package/pipeline/skills/shared/external/core-bluetooth/references/ble-patterns.md +257 -337
  56. package/pipeline/skills/shared/external/core-data/SKILL.md +292 -368
  57. package/pipeline/skills/shared/external/core-data/evals/evals.json +30 -27
  58. package/pipeline/skills/shared/external/core-motion/SKILL.md +235 -324
  59. package/pipeline/skills/shared/external/core-motion/evals/evals.json +31 -27
  60. package/pipeline/skills/shared/external/core-motion/references/motion-patterns.md +210 -310
  61. package/pipeline/skills/shared/external/core-nfc/SKILL.md +292 -366
  62. package/pipeline/skills/shared/external/core-nfc/evals/evals.json +22 -24
  63. package/pipeline/skills/shared/external/core-nfc/references/nfc-patterns.md +233 -329
  64. package/pipeline/skills/shared/external/coreml/SKILL.md +323 -367
  65. package/pipeline/skills/shared/external/coreml/evals/evals.json +24 -21
  66. package/pipeline/skills/shared/external/coreml/references/coreml-swift-integration.md +562 -565
  67. package/pipeline/skills/shared/external/cryptokit/SKILL.md +253 -394
  68. package/pipeline/skills/shared/external/cryptokit/evals/evals.json +20 -18
  69. package/pipeline/skills/shared/external/cryptokit/references/cryptokit-patterns.md +299 -488
  70. package/pipeline/skills/shared/external/debugging-instruments/SKILL.md +270 -323
  71. package/pipeline/skills/shared/external/debugging-instruments/evals/evals.json +27 -30
  72. package/pipeline/skills/shared/external/debugging-instruments/references/instruments-guide.md +167 -315
  73. package/pipeline/skills/shared/external/debugging-instruments/references/lldb-patterns.md +140 -193
  74. package/pipeline/skills/shared/external/device-integrity/SKILL.md +230 -353
  75. package/pipeline/skills/shared/external/device-integrity/evals/evals.json +25 -21
  76. package/pipeline/skills/shared/external/device-integrity/references/device-integrity-patterns.md +159 -197
  77. package/pipeline/skills/shared/external/energykit/SKILL.md +225 -392
  78. package/pipeline/skills/shared/external/energykit/evals/evals.json +29 -28
  79. package/pipeline/skills/shared/external/energykit/references/energykit-patterns.md +174 -470
  80. package/pipeline/skills/shared/external/eventkit-calendar/SKILL.md +261 -383
  81. package/pipeline/skills/shared/external/eventkit-calendar/evals/evals.json +25 -22
  82. package/pipeline/skills/shared/external/eventkit-calendar/references/eventkit-patterns.md +165 -268
  83. package/pipeline/skills/shared/external/healthkit/SKILL.md +252 -303
  84. package/pipeline/skills/shared/external/healthkit/evals/evals.json +24 -23
  85. package/pipeline/skills/shared/external/healthkit/references/healthkit-patterns.md +369 -523
  86. package/pipeline/skills/shared/external/homekit-matter/SKILL.md +233 -348
  87. package/pipeline/skills/shared/external/homekit-matter/evals/evals.json +27 -22
  88. package/pipeline/skills/shared/external/homekit-matter/references/matter-commissioning.md +199 -305
  89. package/pipeline/skills/shared/external/ios-accessibility/SKILL.md +368 -340
  90. package/pipeline/skills/shared/external/ios-accessibility/evals/evals.json +28 -27
  91. package/pipeline/skills/shared/external/ios-accessibility/references/a11y-patterns.md +314 -260
  92. package/pipeline/skills/shared/external/ios-accessibility/references/media-accessibility.md +97 -67
  93. package/pipeline/skills/shared/external/ios-accessibility/references/nutrition-labels.md +165 -101
  94. package/pipeline/skills/shared/external/ios-localization/SKILL.md +258 -371
  95. package/pipeline/skills/shared/external/ios-localization/evals/evals.json +23 -23
  96. package/pipeline/skills/shared/external/ios-localization/references/formatstyle-locale.md +283 -491
  97. package/pipeline/skills/shared/external/ios-localization/references/string-catalogs.md +313 -440
  98. package/pipeline/skills/shared/external/ios-networking/SKILL.md +265 -341
  99. package/pipeline/skills/shared/external/ios-networking/evals/evals.json +24 -24
  100. package/pipeline/skills/shared/external/ios-networking/references/background-websocket.md +425 -652
  101. package/pipeline/skills/shared/external/ios-networking/references/file-storage-patterns.md +143 -285
  102. package/pipeline/skills/shared/external/ios-networking/references/lightweight-clients.md +93 -53
  103. package/pipeline/skills/shared/external/ios-networking/references/network-framework.md +231 -456
  104. package/pipeline/skills/shared/external/ios-networking/references/urlsession-patterns.md +517 -784
  105. package/pipeline/skills/shared/external/ios-simulator/SKILL.md +265 -393
  106. package/pipeline/skills/shared/external/ios-simulator/evals/evals.json +21 -21
  107. package/pipeline/skills/shared/external/ios-simulator/references/simctl-commands.md +177 -270
  108. package/pipeline/skills/shared/external/live-activities/SKILL.md +318 -360
  109. package/pipeline/skills/shared/external/live-activities/evals/evals.json +21 -21
  110. package/pipeline/skills/shared/external/live-activities/references/activitykit-patterns.md +478 -710
  111. package/pipeline/skills/shared/external/mapkit-location/SKILL.md +295 -267
  112. package/pipeline/skills/shared/external/mapkit-location/evals/evals.json +28 -24
  113. package/pipeline/skills/shared/external/mapkit-location/references/mapkit-corelocation-patterns.md +378 -532
  114. package/pipeline/skills/shared/external/mapkit-location/references/mapkit-patterns.md +397 -499
  115. package/pipeline/skills/shared/external/metrickit-diagnostics/SKILL.md +165 -348
  116. package/pipeline/skills/shared/external/metrickit-diagnostics/evals/evals.json +26 -23
  117. package/pipeline/skills/shared/external/metrickit-diagnostics/references/metrickit-patterns.md +123 -130
  118. package/pipeline/skills/shared/external/musickit-audio/SKILL.md +189 -315
  119. package/pipeline/skills/shared/external/musickit-audio/evals/evals.json +22 -21
  120. package/pipeline/skills/shared/external/musickit-audio/references/musickit-patterns.md +181 -270
  121. package/pipeline/skills/shared/external/natural-language/SKILL.md +188 -340
  122. package/pipeline/skills/shared/external/natural-language/evals/evals.json +21 -21
  123. package/pipeline/skills/shared/external/natural-language/references/translation-patterns.md +171 -225
  124. package/pipeline/skills/shared/external/passkit-wallet/SKILL.md +258 -392
  125. package/pipeline/skills/shared/external/passkit-wallet/evals/evals.json +30 -29
  126. package/pipeline/skills/shared/external/passkit-wallet/references/wallet-passes.md +164 -231
  127. package/pipeline/skills/shared/external/pdfkit/SKILL.md +312 -344
  128. package/pipeline/skills/shared/external/pdfkit/evals/evals.json +19 -19
  129. package/pipeline/skills/shared/external/pdfkit/references/pdfkit-patterns.md +413 -624
  130. package/pipeline/skills/shared/external/pencilkit-drawing/SKILL.md +242 -358
  131. package/pipeline/skills/shared/external/pencilkit-drawing/evals/evals.json +25 -21
  132. package/pipeline/skills/shared/external/pencilkit-drawing/references/pencilkit-patterns.md +161 -226
  133. package/pipeline/skills/shared/external/permissionkit/SKILL.md +282 -400
  134. package/pipeline/skills/shared/external/permissionkit/evals/evals.json +27 -30
  135. package/pipeline/skills/shared/external/permissionkit/references/permissionkit-patterns.md +237 -350
  136. package/pipeline/skills/shared/external/photos-camera-media/SKILL.md +276 -325
  137. package/pipeline/skills/shared/external/photos-camera-media/references/av-playback.md +299 -545
  138. package/pipeline/skills/shared/external/photos-camera-media/references/camera-capture.md +344 -588
  139. package/pipeline/skills/shared/external/photos-camera-media/references/image-loading-caching.md +316 -660
  140. package/pipeline/skills/shared/external/photos-camera-media/references/photokit-patterns.md +270 -416
  141. package/pipeline/skills/shared/external/push-notifications/SKILL.md +312 -340
  142. package/pipeline/skills/shared/external/push-notifications/evals/evals.json +27 -26
  143. package/pipeline/skills/shared/external/push-notifications/references/notification-patterns.md +328 -485
  144. package/pipeline/skills/shared/external/push-notifications/references/rich-notifications.md +327 -560
  145. package/pipeline/skills/shared/external/realitykit-ar/SKILL.md +218 -410
  146. package/pipeline/skills/shared/external/realitykit-ar/evals/evals.json +24 -27
  147. package/pipeline/skills/shared/external/realitykit-ar/references/realitykit-patterns.md +221 -348
  148. package/pipeline/skills/shared/external/shareplay-activities/SKILL.md +222 -393
  149. package/pipeline/skills/shared/external/shareplay-activities/evals/evals.json +23 -24
  150. package/pipeline/skills/shared/external/shareplay-activities/references/shareplay-patterns.md +280 -420
  151. package/pipeline/skills/shared/external/speech-recognition/SKILL.md +217 -421
  152. package/pipeline/skills/shared/external/speech-recognition/evals/evals.json +23 -26
  153. package/pipeline/skills/shared/external/speech-recognition/references/speechanalyzer-patterns.md +133 -125
  154. package/pipeline/skills/shared/external/storekit/SKILL.md +228 -204
  155. package/pipeline/skills/shared/external/storekit/evals/evals.json +27 -24
  156. package/pipeline/skills/shared/external/storekit/references/app-review-guidelines.md +98 -109
  157. package/pipeline/skills/shared/external/storekit/references/core-patterns.md +298 -242
  158. package/pipeline/skills/shared/external/storekit/references/storekit-advanced.md +356 -649
  159. package/pipeline/skills/shared/external/swift-api-design-guidelines/SKILL.md +274 -399
  160. package/pipeline/skills/shared/external/swift-api-design-guidelines/evals/evals.json +22 -24
  161. package/pipeline/skills/shared/external/swift-api-design-guidelines/references/argument-labels-and-parameters.md +107 -108
  162. package/pipeline/skills/shared/external/swift-api-design-guidelines/references/conventions-and-special-rules.md +93 -165
  163. package/pipeline/skills/shared/external/swift-api-design-guidelines/references/naming-and-clarity.md +99 -137
  164. package/pipeline/skills/shared/external/swift-api-design-guidelines/references/side-effects-and-mutating-pairs.md +77 -120
  165. package/pipeline/skills/shared/external/swift-architecture/SKILL.md +334 -350
  166. package/pipeline/skills/shared/external/swift-architecture/evals/evals.json +22 -22
  167. package/pipeline/skills/shared/external/swift-charts/SKILL.md +208 -394
  168. package/pipeline/skills/shared/external/swift-charts/evals/evals.json +27 -30
  169. package/pipeline/skills/shared/external/swift-charts/references/charts-patterns.md +351 -762
  170. package/pipeline/skills/shared/external/swift-codable/SKILL.md +339 -343
  171. package/pipeline/skills/shared/external/swift-codable/evals/evals.json +20 -20
  172. package/pipeline/skills/shared/external/swift-concurrency/SKILL.md +303 -351
  173. package/pipeline/skills/shared/external/swift-concurrency/evals/evals.json +27 -24
  174. package/pipeline/skills/shared/external/swift-concurrency/references/approachable-concurrency.md +65 -80
  175. package/pipeline/skills/shared/external/swift-concurrency/references/async-algorithms.md +48 -84
  176. package/pipeline/skills/shared/external/swift-concurrency/references/bridging-interop.md +134 -79
  177. package/pipeline/skills/shared/external/swift-concurrency/references/concurrency-patterns.md +145 -167
  178. package/pipeline/skills/shared/external/swift-concurrency/references/diagnostics.md +62 -50
  179. package/pipeline/skills/shared/external/swift-concurrency/references/swiftui-concurrency.md +92 -121
  180. package/pipeline/skills/shared/external/swift-concurrency/references/synchronization-primitives.md +177 -241
  181. package/pipeline/skills/shared/external/swift-formatstyle/SKILL.md +258 -234
  182. package/pipeline/skills/shared/external/swift-language/SKILL.md +342 -382
  183. package/pipeline/skills/shared/external/swift-language/evals/evals.json +24 -27
  184. package/pipeline/skills/shared/external/swift-language/references/swift-attributes-interop.md +79 -56
  185. package/pipeline/skills/shared/external/swift-language/references/swift-patterns-extended.md +297 -340
  186. package/pipeline/skills/shared/external/swift-security/SKILL.md +180 -161
  187. package/pipeline/skills/shared/external/swift-security/evals/evals.json +25 -25
  188. package/pipeline/skills/shared/external/swift-security/references/biometric-authentication.md +314 -469
  189. package/pipeline/skills/shared/external/swift-security/references/certificate-trust.md +408 -476
  190. package/pipeline/skills/shared/external/swift-security/references/common-anti-patterns.md +260 -530
  191. package/pipeline/skills/shared/external/swift-security/references/compliance-owasp-mapping.md +270 -477
  192. package/pipeline/skills/shared/external/swift-security/references/credential-storage-patterns.md +573 -571
  193. package/pipeline/skills/shared/external/swift-security/references/cryptokit-public-key.md +370 -441
  194. package/pipeline/skills/shared/external/swift-security/references/cryptokit-symmetric.md +332 -433
  195. package/pipeline/skills/shared/external/swift-security/references/keychain-access-control.md +346 -468
  196. package/pipeline/skills/shared/external/swift-security/references/keychain-fundamentals.md +352 -472
  197. package/pipeline/skills/shared/external/swift-security/references/keychain-item-classes.md +431 -432
  198. package/pipeline/skills/shared/external/swift-security/references/keychain-sharing.md +328 -425
  199. package/pipeline/skills/shared/external/swift-security/references/migration-legacy-stores.md +341 -579
  200. package/pipeline/skills/shared/external/swift-security/references/secure-enclave.md +396 -457
  201. package/pipeline/skills/shared/external/swift-security/references/testing-security-code.md +354 -614
  202. package/pipeline/skills/shared/external/swift-testing/SKILL.md +188 -175
  203. package/pipeline/skills/shared/external/swift-testing/evals/evals.json +26 -24
  204. package/pipeline/skills/shared/external/swift-testing/references/testing-advanced.md +80 -84
  205. package/pipeline/skills/shared/external/swift-testing/references/testing-patterns.md +317 -433
  206. package/pipeline/skills/shared/external/swiftdata/SKILL.md +392 -256
  207. package/pipeline/skills/shared/external/swiftdata/evals/evals.json +24 -24
  208. package/pipeline/skills/shared/external/swiftdata/references/core-data-coexistence.md +206 -402
  209. package/pipeline/skills/shared/external/swiftdata/references/indexing.md +59 -52
  210. package/pipeline/skills/shared/external/swiftdata/references/predicate-pitfalls.md +57 -33
  211. package/pipeline/skills/shared/external/swiftdata/references/swiftdata-advanced.md +354 -747
  212. package/pipeline/skills/shared/external/swiftdata/references/swiftdata-queries.md +300 -508
  213. package/pipeline/skills/shared/external/swiftlint/SKILL.md +175 -226
  214. package/pipeline/skills/shared/external/swiftlint/references/adoption-and-configuration.md +141 -208
  215. package/pipeline/skills/shared/external/swiftlint/references/custom-rules-and-analyze.md +100 -109
  216. package/pipeline/skills/shared/external/swiftlint/references/plugins-run-scripts-and-integrations.md +159 -179
  217. package/pipeline/skills/shared/external/swiftlint/references/rule-reference.md +383 -18
  218. package/pipeline/skills/shared/external/swiftlint/references/rules-suppressions-and-baselines.md +143 -229
  219. package/pipeline/skills/shared/external/swiftui-animation/SKILL.md +283 -366
  220. package/pipeline/skills/shared/external/swiftui-animation/references/animation-advanced.md +396 -608
  221. package/pipeline/skills/shared/external/swiftui-animation/references/core-animation-bridge.md +336 -385
  222. package/pipeline/skills/shared/external/swiftui-gestures/SKILL.md +239 -349
  223. package/pipeline/skills/shared/external/swiftui-gestures/references/gesture-patterns.md +228 -310
  224. package/pipeline/skills/shared/external/swiftui-layout-components/SKILL.md +260 -249
  225. package/pipeline/skills/shared/external/swiftui-layout-components/references/form.md +92 -74
  226. package/pipeline/skills/shared/external/swiftui-layout-components/references/grids.md +112 -177
  227. package/pipeline/skills/shared/external/swiftui-layout-components/references/list.md +61 -64
  228. package/pipeline/skills/shared/external/swiftui-layout-components/references/scrollview.md +94 -134
  229. package/pipeline/skills/shared/external/swiftui-liquid-glass/SKILL.md +193 -225
  230. package/pipeline/skills/shared/external/swiftui-liquid-glass/references/liquid-glass.md +173 -327
  231. package/pipeline/skills/shared/external/swiftui-navigation/SKILL.md +193 -168
  232. package/pipeline/skills/shared/external/swiftui-navigation/references/deeplinks.md +127 -150
  233. package/pipeline/skills/shared/external/swiftui-navigation/references/navigationstack.md +132 -133
  234. package/pipeline/skills/shared/external/swiftui-navigation/references/sheets.md +152 -117
  235. package/pipeline/skills/shared/external/swiftui-navigation/references/tabview.md +106 -140
  236. package/pipeline/skills/shared/external/swiftui-patterns/SKILL.md +316 -252
  237. package/pipeline/skills/shared/external/swiftui-patterns/references/architecture-patterns.md +341 -332
  238. package/pipeline/skills/shared/external/swiftui-patterns/references/deprecated-migration.md +547 -854
  239. package/pipeline/skills/shared/external/swiftui-patterns/references/design-polish.md +485 -537
  240. package/pipeline/skills/shared/external/swiftui-patterns/references/platform-and-sharing.md +417 -499
  241. package/pipeline/skills/shared/external/swiftui-performance/SKILL.md +213 -376
  242. package/pipeline/skills/shared/external/swiftui-performance/references/demystify-swiftui-performance-wwdc23.md +86 -175
  243. package/pipeline/skills/shared/external/swiftui-performance/references/optimizing-swiftui-performance-instruments.md +89 -195
  244. package/pipeline/skills/shared/external/swiftui-performance/references/understanding-hangs-in-your-app.md +95 -182
  245. package/pipeline/skills/shared/external/swiftui-performance/references/understanding-improving-swiftui-performance.md +71 -149
  246. package/pipeline/skills/shared/external/swiftui-performance/references/wwdc-session-sources.md +21 -27
  247. package/pipeline/skills/shared/external/swiftui-uikit-interop/SKILL.md +303 -295
  248. package/pipeline/skills/shared/external/swiftui-uikit-interop/references/hosting-migration.md +204 -387
  249. package/pipeline/skills/shared/external/swiftui-uikit-interop/references/representable-recipes.md +469 -683
  250. package/pipeline/skills/shared/external/swiftui-webkit/SKILL.md +140 -186
  251. package/pipeline/skills/shared/external/swiftui-webkit/references/loading-and-observation.md +75 -86
  252. package/pipeline/skills/shared/external/swiftui-webkit/references/local-content-and-custom-schemes.md +63 -60
  253. package/pipeline/skills/shared/external/swiftui-webkit/references/migration-and-fallbacks.md +69 -137
  254. package/pipeline/skills/shared/external/swiftui-webkit/references/navigation-and-javascript.md +95 -67
  255. package/pipeline/skills/shared/external/tipkit/SKILL.md +220 -335
  256. package/pipeline/skills/shared/external/tipkit/references/tipkit-patterns.md +356 -494
  257. package/pipeline/skills/shared/external/vision-framework/SKILL.md +260 -375
  258. package/pipeline/skills/shared/external/vision-framework/references/vision-requests.md +393 -515
  259. package/pipeline/skills/shared/external/vision-framework/references/visionkit-scanner.md +363 -539
  260. package/pipeline/skills/shared/external/weatherkit/SKILL.md +152 -310
  261. package/pipeline/skills/shared/external/weatherkit/references/weatherkit-patterns.md +288 -407
  262. package/pipeline/skills/shared/external/widgetkit/SKILL.md +216 -288
  263. package/pipeline/skills/shared/external/widgetkit/references/widgetkit-advanced.md +414 -719
  264. package/pipeline/skills/shared/external/NOTICE-swift-ios-skills.md +0 -39
@@ -1,439 +1,316 @@
1
1
  ---
2
2
  name: device-integrity
3
- description: "Verify device legitimacy and app integrity using DeviceCheck (DCDevice per-device bits) and App Attest (DCAppAttestService key generation, attestation, and assertion flows). Use when implementing fraud prevention, detecting compromised devices, validating app authenticity with Apple's servers, protecting sensitive API endpoints with attested requests, or adding device verification to a backend architecture."
3
+ description: "DeviceCheck and App Attest: DCDevice tokens and per-device bits, DCAppAttestService key generation, attestation and assertions. Use when adding fraud prevention, detecting compromised devices, validating app authenticity with Apple's servers, protecting sensitive endpoints with attested requests, or designing backend device verification. Not for sign-in, OAuth, JWT sessions, TLS or pinning."
4
4
  metadata:
5
- source: "dpearson2699/swift-ios-skills (PolyForm Perimeter 1.0.0)"
5
+ source: multi-agent-pipeline
6
6
  ---
7
7
 
8
8
  # Device Integrity
9
9
 
10
- Verify that requests to your server come from a genuine Apple device running a
11
- legitimate instance of your app. DeviceCheck provides per-device bits for
12
- simple flags (e.g., "claimed promo offer"). App Attest uses Secure Enclave keys
13
- and Apple attestation to cryptographically prove app legitimacy on sensitive
14
- requests.
10
+ Two frameworks let a backend decide whether a request really originates from
11
+ real Apple hardware running an unmodified build of your app:
15
12
 
16
- ## Contents
13
+ - **DeviceCheck** (`DCDevice`) hands your server a token it can trade with Apple
14
+ for two developer-defined bits per device. Good for simple flags such as
15
+ "this phone already redeemed the trial".
16
+ - **App Attest** (`DCAppAttestService`) creates a hardware key inside the Secure
17
+ Enclave, has Apple certify it, and then signs chosen requests with it. Good
18
+ for cryptographic proof on sensitive calls.
17
19
 
18
- - [DCDevice (DeviceCheck Tokens)](#dcdevice-devicecheck-tokens)
19
- - [DCAppAttestService (App Attest)](#dcappattestservice-app-attest)
20
- - [App Attest Key Generation](#app-attest-key-generation)
21
- - [App Attest Attestation Flow](#app-attest-attestation-flow)
22
- - [App Attest Assertion Flow](#app-attest-assertion-flow)
23
- - [Server Verification Guidance](#server-verification-guidance)
24
- - [Error Handling](#error-handling)
25
- - [Common Patterns](#common-patterns)
26
- - [Common Mistakes](#common-mistakes)
27
- - [Review Checklist](#review-checklist)
28
- - [References](#references)
20
+ Server algorithms, retries, rejected-key recovery, an integration outline and
21
+ rollout advice live in the [patterns reference](references/device-integrity-patterns.md).
22
+ The `IntegrityError` enum used below is defined there too.
29
23
 
30
- ## DCDevice (DeviceCheck Tokens)
24
+ ## DeviceCheck tokens (`DCDevice`, iOS 11+)
31
25
 
32
- [`DCDevice`](https://sosumi.ai/documentation/devicecheck/dcdevice) generates a
33
- unique, ephemeral token that identifies a device. Treat each token as
34
- single-use: generate a new token for each server operation instead of caching or
35
- reusing one. The token is sent to your server, which then communicates with
36
- Apple's servers to read or set two per-device bits. Available on iOS 11+.
37
-
38
- ### Token Generation
26
+ `DCDevice` produces an ephemeral token that identifies the device to Apple.
27
+ Each token is single-use: create a fresh one for every server operation and
28
+ never cache it. The server forwards the token to Apple to read or change the
29
+ two bits.
39
30
 
40
31
  ```swift
41
32
  import DeviceCheck
42
33
 
43
- func generateDeviceToken() async throws -> Data {
44
- guard DCDevice.current.isSupported else {
45
- throw DeviceIntegrityError.deviceCheckUnsupported
46
- }
47
-
48
- return try await DCDevice.current.generateToken()
34
+ func sendJSON(_ fields: [String: String], to url: URL) async throws -> Bool {
35
+ var req = URLRequest(url: url)
36
+ req.httpMethod = "POST"
37
+ req.addValue("application/json", forHTTPHeaderField: "Content-Type")
38
+ let payload = try JSONEncoder().encode(fields)
39
+ let (_, reply) = try await URLSession.shared.upload(for: req, from: payload)
40
+ return (reply as? HTTPURLResponse).map { $0.statusCode == 200 } ?? false
49
41
  }
50
- ```
51
-
52
- ### Sending the Token to Your Server
53
42
 
54
- ```swift
55
- func sendTokenToServer(_ token: Data) async throws {
56
- let tokenString = token.base64EncodedString()
57
-
58
- var request = URLRequest(url: serverURL.appending(path: "verify-device"))
59
- request.httpMethod = "POST"
60
- request.setValue("application/json", forHTTPHeaderField: "Content-Type")
61
- request.httpBody = try JSONEncoder().encode(["device_token": tokenString])
62
-
63
- let (_, response) = try await URLSession.shared.data(for: request)
64
- guard let httpResponse = response as? HTTPURLResponse,
65
- httpResponse.statusCode == 200 else {
66
- throw DeviceIntegrityError.serverVerificationFailed
67
- }
43
+ func claimTrial(at endpoint: URL) async throws {
44
+ guard DCDevice.current.isSupported else { throw IntegrityError.deviceCheckUnsupported }
45
+ let token = try await DCDevice.current.generateToken() // new token every call
46
+ let accepted = try await sendJSON(["device_token": token.base64EncodedString()], to: endpoint)
47
+ if !accepted { throw IntegrityError.serverVerificationFailed }
68
48
  }
69
49
  ```
70
50
 
71
- ### Server-Side Overview
72
-
73
- Your server uses the device token to call Apple's DeviceCheck API endpoints:
74
-
75
- | Endpoint | Purpose |
76
- |----------|---------|
77
- | `https://api.devicecheck.apple.com/v1/query_two_bits` | Read the two bits for a device |
78
- | `https://api.devicecheck.apple.com/v1/update_two_bits` | Set the two bits for a device |
79
- | `https://api.devicecheck.apple.com/v1/validate_device_token` | Validate a device token without reading bits |
51
+ ### What the server does
80
52
 
81
- The server authenticates with a DeviceCheck private key from the Apple Developer
82
- portal, creating a signed JWT for each request.
53
+ All three calls live under `/v1/` on the DeviceCheck host:
83
54
 
84
- Use `https://api.development.devicecheck.apple.com` only while testing; use
85
- `https://api.devicecheck.apple.com` for production.
55
+ | Path | Purpose |
56
+ |---|---|
57
+ | `query_two_bits` | Returns the current bit values |
58
+ | `update_two_bits` | Changes one or both bits |
59
+ | `validate_device_token` | Confirms a token is real, bits untouched |
86
60
 
87
- ### What the Two Bits Are For
61
+ The server signs a JWT for each call with a DeviceCheck private key created in
62
+ the Developer portal. Hosts: `api.devicecheck.apple.com` for production traffic,
63
+ and `api.development.devicecheck.apple.com` only while testing.
88
64
 
89
- Apple stores two Boolean values per device per developer team. You decide what
90
- they mean. Common uses:
65
+ ### The two bits
91
66
 
92
- - **Bit 0:** Device has claimed a promotional offer.
93
- - **Bit 1:** Device has been flagged for fraud.
67
+ Apple keeps two Booleans per device for each developer team, and you decide
68
+ what they mean. For example bit 0 might record "promo redeemed" and bit 1
69
+ "flagged for abuse". They survive deleting and reinstalling the app; only
70
+ your server can reset them through the API.
94
71
 
95
- Bits persist across app reinstall. You control when to reset them via the
96
- server API.
72
+ ## App Attest (`DCAppAttestService`, iOS 14+)
97
73
 
98
- ## DCAppAttestService (App Attest)
74
+ App Attest ties a specific app instance on a specific device to a Secure
75
+ Enclave key. It runs in three phases:
99
76
 
100
- [`DCAppAttestService`](https://sosumi.ai/documentation/devicecheck/dcappattestservice)
101
- validates that a specific instance of your app on a specific device is
102
- legitimate. It uses a hardware-backed key in the Secure Enclave to create
103
- cryptographic attestations and assertions. Available on iOS 14+.
77
+ 1. **Key generation** - the Secure Enclave makes a key pair.
78
+ 2. **Attestation** - Apple vouches that this key sits on real hardware inside
79
+ your app.
80
+ 3. **Assertion** - sign chosen requests with the attested key.
104
81
 
105
- The flow has three phases:
106
- 1. **Key generation** -- create a key pair in the Secure Enclave.
107
- 2. **Attestation** -- Apple certifies the key belongs to a genuine Apple device running your app.
108
- 3. **Assertion** -- sign server requests with the attested key to prove ongoing legitimacy.
109
-
110
- ### Checking Support
82
+ ### Support check
111
83
 
112
84
  ```swift
113
- import DeviceCheck
114
-
115
- let attestService = DCAppAttestService.shared
116
-
117
- guard attestService.isSupported else {
118
- // Fall back to DCDevice token or other risk assessment.
119
- // App Attest is not available on simulators or all device models.
120
- return
85
+ let attest = DCAppAttestService.shared
86
+ if !attest.isSupported {
87
+ // Fall back to a DCDevice token or another risk signal.
121
88
  }
122
89
  ```
123
90
 
124
- For app extensions, App Attest is supported only in Action, extensible SSO, and
125
- watchOS extensions. Treat other extension types as unsupported even if
126
- `isSupported` returns `true`.
91
+ The service is unavailable in the Simulator and on some hardware. For app
92
+ extensions only Action, extensible single sign-on and watchOS extensions are
93
+ supported; treat any other extension type as unsupported even when
94
+ `isSupported` returns true.
127
95
 
128
- ## App Attest Key Generation
96
+ ### Key generation
129
97
 
130
- Generate one cryptographic key pair per user account on each device. The
131
- private key stays in the Secure Enclave. The returned `keyId` is the only
132
- identifier your app can later use to access the key, so record and reuse the
133
- account/device-scoped `keyId`; do not share one key across users. Avoid
134
- unnecessary regeneration because each new key affects App Attest key-count risk
135
- metrics. Only treat the `keyId` as usable after your server verifies
136
- attestation. If server verification fails, discard the `keyId` and generate a
98
+ Make one key for each signed-in account on a given device. Its private half never leaves
99
+ the Secure Enclave and the returned `keyId` is your only handle to it, so store
100
+ it and reuse it for that account on that device. Never share a key between
101
+ users, and avoid regenerating: every new key moves Apple's per-device key
102
+ count, which feeds risk metrics. Treat a `keyId` as usable only once your
103
+ server accepts its attestation; if verification fails, discard it and make a
137
104
  new key before retrying.
138
105
 
139
106
  ```swift
140
107
  import DeviceCheck
108
+ import Foundation
109
+ import Security
141
110
 
142
- actor AppAttestManager {
111
+ actor AttestKeyStore {
143
112
  private let service = DCAppAttestService.shared
144
- private var keyId: String?
113
+ private let accountID: String
114
+ var keyID: String?
145
115
 
146
- /// Generate and record a key pair for App Attest.
147
- func generateKeyIfNeeded() async throws -> String {
148
- if let existingKeyId = loadKeyIdFromKeychain() {
149
- self.keyId = existingKeyId
150
- return existingKeyId
151
- }
152
-
153
- let newKeyId = try await service.generateKey()
154
- saveKeyIdToKeychain(newKeyId)
155
- self.keyId = newKeyId
156
- return newKeyId
157
- }
116
+ init(accountID: String) { self.accountID = accountID }
158
117
 
159
- // MARK: - Keychain helpers (simplified)
118
+ private var keychainAccount: String { "attest-key.\(accountID)" }
160
119
 
161
- private func saveKeyIdToKeychain(_ keyId: String) {
162
- let data = Data(keyId.utf8)
163
- let query: [String: Any] = [
120
+ var baseQuery: [String: Any] {
121
+ [
164
122
  kSecClass as String: kSecClassGenericPassword,
165
- kSecAttrAccount as String: "app-attest-key-id-\(currentAccountID)",
166
- kSecAttrService as String: Bundle.main.bundleIdentifier ?? "",
167
- kSecValueData as String: data,
168
- kSecAttrAccessible as String: kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly
123
+ kSecAttrService as String: Bundle.main.bundleIdentifier ?? "attest",
124
+ kSecAttrAccount as String: keychainAccount,
169
125
  ]
170
- SecItemDelete(query as CFDictionary) // Remove old if exists
171
- SecItemAdd(query as CFDictionary, nil)
172
126
  }
173
127
 
174
- private func loadKeyIdFromKeychain() -> String? {
175
- let query: [String: Any] = [
176
- kSecClass as String: kSecClassGenericPassword,
177
- kSecAttrAccount as String: "app-attest-key-id-\(currentAccountID)",
178
- kSecAttrService as String: Bundle.main.bundleIdentifier ?? "",
179
- kSecReturnData as String: true,
180
- kSecMatchLimit as String: kSecMatchLimitOne
181
- ]
182
- var result: AnyObject?
183
- let status = SecItemCopyMatching(query as CFDictionary, &result)
184
- guard status == errSecSuccess, let data = result as? Data else { return nil }
185
- return String(data: data, encoding: .utf8)
186
- }
187
- }
188
- ```
189
-
190
- **Important:** Generate the key once per user account on a device, persist that
191
- account/device `keyId`, and keep the key count low. Generating unnecessary keys
192
- pollutes App Attest risk metrics.
193
-
194
- ## App Attest Attestation Flow
195
-
196
- Attestation proves that the key was generated on a genuine Apple device running
197
- a legitimate instance of your app. You perform attestation once per key, then
198
- store the verified public key and receipt on your server. The app stores the
199
- `keyId` for future assertions after the server accepts the attestation.
200
-
201
- ### Client-Side Attestation
202
-
203
- ```swift
204
- import DeviceCheck
205
- import CryptoKit
206
-
207
- extension AppAttestManager {
208
- /// Attest the key with Apple. Send the attestation object to your server.
209
- func attestKey() async throws -> Data {
210
- guard let keyId else {
211
- throw DeviceIntegrityError.keyNotGenerated
128
+ func generateKeyIfNeeded() async throws -> String {
129
+ if let keyID { return keyID }
130
+ if let stored = loadKeyID() {
131
+ keyID = stored
132
+ return stored
212
133
  }
213
-
214
- // 1. Request a one-time challenge from your server
215
- let challenge = try await fetchServerChallenge()
216
-
217
- // 2. Hash the challenge (Apple requires a SHA-256 hash)
218
- let challengeHash = Data(SHA256.hash(data: challenge))
219
-
220
- // 3. Ask Apple to attest the key
221
- let attestation = try await service.attestKey(keyId, clientDataHash: challengeHash)
222
-
223
- // 4. Send the attestation object to your server for verification
224
- try await sendAttestationToServer(
225
- keyId: keyId,
226
- attestation: attestation,
227
- challenge: challenge
228
- )
229
-
230
- return attestation
134
+ let fresh = try await service.generateKey()
135
+ try saveKeyID(fresh)
136
+ keyID = fresh
137
+ return fresh
231
138
  }
232
139
 
233
- private func fetchServerChallenge() async throws -> Data {
234
- let url = serverURL.appending(path: "attest/challenge")
235
- let (data, _) = try await URLSession.shared.data(from: url)
236
- return data
140
+ private func saveKeyID(_ value: String) throws {
141
+ SecItemDelete(baseQuery as CFDictionary)
142
+ var item = baseQuery
143
+ item[kSecValueData as String] = Data(value.utf8)
144
+ item[kSecAttrAccessible as String] = kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly
145
+ let status = SecItemAdd(item as CFDictionary, nil)
146
+ guard status == errSecSuccess else { throw IntegrityError.keyNotGenerated }
237
147
  }
238
148
 
239
- private func sendAttestationToServer(
240
- keyId: String,
241
- attestation: Data,
242
- challenge: Data
243
- ) async throws {
244
- var request = URLRequest(url: serverURL.appending(path: "attest/verify"))
245
- request.httpMethod = "POST"
246
- request.setValue("application/json", forHTTPHeaderField: "Content-Type")
247
-
248
- let payload: [String: String] = [
249
- "key_id": keyId,
250
- "attestation": attestation.base64EncodedString(),
251
- "challenge": challenge.base64EncodedString()
252
- ]
253
- request.httpBody = try JSONEncoder().encode(payload)
254
-
255
- let (_, response) = try await URLSession.shared.data(for: request)
256
- guard let httpResponse = response as? HTTPURLResponse,
257
- httpResponse.statusCode == 200 else {
258
- throw DeviceIntegrityError.attestationVerificationFailed
259
- }
149
+ private func loadKeyID() -> String? {
150
+ var query = baseQuery
151
+ query[kSecReturnData as String] = true
152
+ query[kSecMatchLimit as String] = kSecMatchLimitOne
153
+ var found: CFTypeRef?
154
+ let status = SecItemCopyMatching(query as CFDictionary, &found)
155
+ guard status == errSecSuccess, let bytes = found as? Data else { return nil }
156
+ return String(data: bytes, encoding: .utf8)
260
157
  }
261
158
  }
262
159
  ```
263
160
 
264
- ### Server-Side Attestation Verification
161
+ Save, load and delete all build on the same `baseQuery`, so the account
162
+ attribute always carries the account suffix. The delete path in the
163
+ [rejected-key section](references/device-integrity-patterns.md#rejected-keys)
164
+ reuses it for that reason.
265
165
 
266
- Your server validates the attestation object (CBOR), verifies the certificate
267
- chain against Apple's App Attest root CA, checks Apple's nonce calculation, and
268
- stores the verified public key and receipt for future assertion verification.
269
- The attestation nonce is not `SHA256(challenge)` alone; it is
270
- `SHA256(authData || SHA256(challenge))` and is compared with the credential
271
- certificate extension `1.2.840.113635.100.8.2`. See
272
- [references/device-integrity-patterns.md](references/device-integrity-patterns.md)
273
- for the full server verification flow.
166
+ ### Attestation
274
167
 
275
- ## App Attest Assertion Flow
168
+ Attestation proves the key was created on a real device inside your genuine
169
+ app. Do it once per key. The server records the public key and receipt it verified;
170
+ the app keeps using the `keyId` for assertions once the server has accepted it.
276
171
 
277
- After attestation, use assertions to sign sensitive requests. Each assertion
278
- proves the request came from the attested app instance and includes a
279
- server-issued, one-time challenge to prevent replay.
280
-
281
- ### Client-Side Assertion
172
+ Client order: ask the server for a one-time challenge, hash it with SHA-256,
173
+ call `attestKey(_:clientDataHash:)`, and upload the result.
282
174
 
283
175
  ```swift
284
- import DeviceCheck
285
176
  import CryptoKit
286
177
 
287
- extension AppAttestManager {
288
- /// Generate an assertion for encoded client data.
289
- /// Client data should include a one-time server challenge and request context.
290
- func generateAssertion(for clientData: Data) async throws -> Data {
291
- guard let keyId else {
292
- throw DeviceIntegrityError.keyNotGenerated
293
- }
294
-
295
- let clientDataHash = Data(SHA256.hash(data: clientData))
178
+ extension AttestKeyStore {
179
+ func attest(challenge: Data, uploadTo url: URL) async throws {
180
+ let keyID = try await generateKeyIfNeeded()
181
+ let hash = Data(SHA256.hash(data: challenge))
182
+ let attestation = try await DCAppAttestService.shared.attestKey(keyID, clientDataHash: hash)
296
183
 
297
- return try await service.generateAssertion(keyId, clientDataHash: clientDataHash)
184
+ let accepted = try await sendJSON([
185
+ "key_id": keyID,
186
+ "attestation": attestation.base64EncodedString(),
187
+ "challenge": challenge.base64EncodedString(),
188
+ ], to: url)
189
+ guard accepted else { throw IntegrityError.attestationVerificationFailed }
298
190
  }
299
191
  }
300
192
  ```
301
193
 
302
- ### Using Assertions in Network Requests
194
+ On the server: parse the CBOR object, walk its certificates up to Apple's
195
+ App Attest root, check the nonce, then persist the public key and receipt. The
196
+ nonce is **not** `SHA256(challenge)`. It is `SHA256(authData || SHA256(challenge))`,
197
+ and it has to match the octet string stored under OID `1.2.840.113635.100.8.2`
198
+ in the leaf certificate.
199
+
200
+ ### Assertion
201
+
202
+ An assertion signs one sensitive request and embeds a server-issued, one-time
203
+ challenge so a captured request cannot be replayed. Call
204
+ `generateAssertion(_:clientDataHash:)` with the SHA-256 of encoded client data
205
+ that holds the challenge plus request context.
303
206
 
304
207
  ```swift
305
- struct AppAttestClientData: Encodable {
208
+ struct SignedRequestContext: Codable {
306
209
  let challenge: String
307
210
  let method: String
308
211
  let path: String
309
- let bodySHA256: String
212
+ let bodyDigest: String
310
213
  }
311
214
 
312
- extension AppAttestManager {
313
- /// Perform an attested API request.
314
- func makeAttestedRequest(
315
- to url: URL,
316
- method: String = "POST",
317
- body: Data
318
- ) async throws -> (Data, URLResponse) {
319
- let challenge = try await fetchAssertionChallenge()
320
- let bodyHash = Data(SHA256.hash(data: body)).base64EncodedString()
321
- let clientData = try JSONEncoder().encode(
322
- AppAttestClientData(
323
- challenge: challenge,
324
- method: method,
325
- path: url.path,
326
- bodySHA256: bodyHash
327
- )
215
+ extension AttestKeyStore {
216
+ func signed(_ original: URLRequest, body: Data, challenge: Data) async throws -> URLRequest {
217
+ var request = original
218
+ let keyID = try await generateKeyIfNeeded()
219
+ let context = SignedRequestContext(
220
+ challenge: challenge.base64EncodedString(),
221
+ method: request.httpMethod ?? "GET",
222
+ path: request.url?.path ?? "/",
223
+ bodyDigest: SHA256.hash(data: body).map { String(format: "%02x", $0) }.joined()
328
224
  )
329
- let assertion = try await generateAssertion(for: clientData)
330
-
331
- var request = URLRequest(url: url)
332
- request.httpMethod = method
333
- request.setValue("application/json", forHTTPHeaderField: "Content-Type")
334
- request.setValue(assertion.base64EncodedString(), forHTTPHeaderField: "X-App-Attest-Assertion")
335
- request.setValue(clientData.base64EncodedString(), forHTTPHeaderField: "X-App-Attest-Client-Data")
225
+ let encoded = try JSONEncoder().encode(context)
226
+ let assertion = try await DCAppAttestService.shared.generateAssertion(
227
+ keyID, clientDataHash: Data(SHA256.hash(data: encoded))
228
+ )
229
+ let headers = [
230
+ "X-App-Attest-Assertion": assertion.base64EncodedString(),
231
+ "X-App-Attest-Client-Data": encoded.base64EncodedString(),
232
+ ]
233
+ headers.forEach { request.addValue($0.value, forHTTPHeaderField: $0.key) }
336
234
  request.httpBody = body
337
-
338
- return try await URLSession.shared.data(for: request)
339
- }
340
-
341
- private func fetchAssertionChallenge() async throws -> String {
342
- let url = serverURL.appending(path: "assert/challenge")
343
- let (data, _) = try await URLSession.shared.data(from: url)
344
- return String(decoding: data, as: UTF8.self)
235
+ return request
345
236
  }
346
237
  }
347
238
  ```
348
239
 
349
- ### Server-Side Assertion Verification
350
-
351
- Your server decodes the assertion (CBOR), verifies the authenticator data and
352
- counter, recomputes `clientDataHash` from the submitted client data, verifies
353
- the signature over `SHA256(authenticatorData || clientDataHash)` with the
354
- stored public key, and confirms the embedded challenge and request context. See
355
- [references/device-integrity-patterns.md](references/device-integrity-patterns.md)
356
- for step-by-step server verification.
357
-
358
- ## Server Verification Guidance
359
-
360
- See [references/device-integrity-patterns.md](references/device-integrity-patterns.md) for full server architecture guidance including attestation vs. assertion comparison, recommended endpoint design, and risk assessment.
361
-
362
- ### Security Boundaries
363
-
364
- App Attest proves app-instance integrity for selected requests. It does not
365
- replace user authentication, OAuth/JWT/session handling, API token design,
366
- entitlement or subscription authorization, TLS, certificate pinning, or general
367
- networking security. Treat those as handoffs to authentication, networking, or
368
- broader security guidance, and still enforce normal authentication and
369
- authorization after App Attest passes.
370
-
371
- ## Error Handling
372
-
373
- Handle `DCError` codes from DeviceCheck operations. Key cases:
374
-
375
- - `.serverUnavailable` - retry with exponential backoff
376
- - `.invalidKey` - the key was already attested, assertion used an unattested key, or the service rejected the key
377
- - `.featureUnsupported` - fall back to `DCDevice` tokens
378
- - `.invalidInput` - malformed `clientDataHash` or `keyId`
379
-
380
- For `attestKey`, retry `.serverUnavailable` later with the same `keyId` and the
381
- same `clientDataHash`. For other attestation errors, discard the key identifier
382
- and create a new key before retrying. See
383
- [references/device-integrity-patterns.md](references/device-integrity-patterns.md)
384
- for full error handling code, retry strategy, and rejected-key recovery.
385
-
386
- ## Common Patterns
387
-
388
- ### Environment Entitlement
389
-
390
- Set the App Attest environment in your entitlements file. Use `development`
391
- during testing and `production` for App Store builds:
392
-
393
- ```xml
394
- <key>com.apple.developer.devicecheck.appattest-environment</key>
395
- <string>production</string>
396
- ```
397
-
398
- When the entitlement is omitted during development, the app uses the App Attest
399
- sandbox by default. After distribution through TestFlight, the App Store, or the
400
- Apple Developer Enterprise Program, the app ignores the entitlement value and
401
- uses production.
402
-
403
- See [references/device-integrity-patterns.md](references/device-integrity-patterns.md) for the full integration manager pattern, gradual rollout guidance, and error type definition.
404
-
405
- ## Common Mistakes
406
-
407
- 1. **Generating a new key on every launch.** Generate once per user account on a device, persist the `keyId`, and keep key counts low.
408
- 2. **Reusing `DCDevice` tokens.** Treat generated tokens as single-use. Generate a new token for each server operation.
409
- 3. **Skipping the fallback for unsupported devices or extensions.** Not all devices and extension types support App Attest. Use `DCDevice` tokens or other risk assessment as fallback.
410
- 4. **Trusting attestation client-side.** All verification must happen on your server.
411
- 5. **Signing only the raw request body.** Assertion client data must include a one-time server challenge and enough request context for the server to bind the assertion to the request.
412
- 6. **Verifying the wrong attestation nonce.** Compare the certificate extension with `SHA256(authData || SHA256(challenge))`, not `SHA256(challenge)` alone.
413
- 7. **Not implementing replay protection.** The server must validate one-time challenges and track the assertion counter.
414
- 8. **Mixing development and production environments.** Sandbox keys and receipts do not work in production, and production keys and receipts do not work in sandbox.
415
- 9. **Not handling `DCError.invalidKey`.** Check for repeated attestation, unattested assertion keys, or service rejection; regenerate only after the state is known bad.
416
-
417
- ## Review Checklist
418
-
419
- - [ ] `DCDevice` tokens generated per server operation and never cached for reuse
420
- - [ ] `DCAppAttestService.isSupported` checked before use; unsupported devices and extension types have a fallback
421
- - [ ] Key generated once per user account on each device and `keyId` persisted only for that app account/device
422
- - [ ] Attestation performed once per key; server stores verified public key and receipt
423
- - [ ] Server validates attestation certificate chain, App ID hash, environment `aaguid`, credential ID, and nonce `SHA256(authData || SHA256(challenge))`
424
- - [ ] Assertions include one-time challenge plus request context; server verifies signature, RP ID, counter, challenge, and request binding
425
- - [ ] Protected endpoints still enforce normal user authentication and entitlement authorization after App Attest passes
426
- - [ ] `DCError` cases handled: `.serverUnavailable` retries attestation with the same key/hash; bad keys are discarded and regenerated
427
- - [ ] App Attest environment entitlement and sandbox/production server routing are consistent
428
- - [ ] Gradual rollout considered; feature flag in place for enabling/disabling
240
+ Fetch a new assertion challenge from your challenge endpoint for every request.
241
+
242
+ Server side: decode the CBOR assertion, inspect authenticator data and its
243
+ counter, hash the client data it received to get `clientDataHash` again, check
244
+ that the stored public key signed `SHA256(authenticatorData || clientDataHash)`,
245
+ and make sure the challenge and the request context both line up.
246
+
247
+ ## Security boundaries
248
+
249
+ App Attest says "this request came from a genuine instance of the app". It
250
+ says nothing about who the user is. Sign-in, OAuth or JWT sessions, how API
251
+ tokens are designed, paid-feature and subscription checks, TLS and certificate
252
+ pinning all remain separate jobs. Keep enforcing authentication and authorization after the
253
+ attestation check passes. Hand-offs:
254
+
255
+ - Apple ID sign-in, passkeys, OAuth flows, credential state: `authentication`
256
+ - Keychain policy, Secure Enclave, pinning, storage hardening: `swift-security`
257
+ - Hashing and signing primitives: `cryptokit`
258
+ - Transport, request pipelines, TLS configuration: `ios-networking`
259
+
260
+ ## Errors
261
+
262
+ | `DCError.Code` | Meaning | Action |
263
+ |---|---|---|
264
+ | `.serverUnavailable` | Apple's service is temporarily unreachable | Retry with exponential backoff |
265
+ | `.invalidKey` | Key already attested, assertion with an unattested key, or key rejected | Find out which, then recover |
266
+ | `.featureUnsupported` | Device or OS lacks App Attest | Use `DCDevice` tokens instead |
267
+ | `.invalidInput` | Malformed `clientDataHash` or `keyId` | Fix the caller |
268
+
269
+ When `attestKey` reports `.serverUnavailable`, try again later passing the
270
+ **same** `keyId` and the **same** `clientDataHash`. Any other attestation
271
+ failure means the key identifier is spent: drop it and make a fresh key first.
272
+
273
+ ## Environment entitlement
274
+
275
+ The App Attest environment entitlement
276
+ (`com.apple.developer.devicecheck.appattest-environment`) takes `development`
277
+ for testing or `production` for release. Without it, development builds talk
278
+ to the App Attest sandbox. TestFlight, App Store and Enterprise Program builds
279
+ ignore the value and always use production.
280
+
281
+ ## Common mistakes
282
+
283
+ - Generating a key on every launch instead of once per account per device.
284
+ - Caching or reusing a `DCDevice` token.
285
+ - No fallback path for unsupported devices or extension types.
286
+ - Trusting attestation on the client. Only the server can verify it.
287
+ - Signing just the raw body. Include the one-time challenge and request context.
288
+ - Checking the nonce as `SHA256(challenge)`.
289
+ - No replay defense. The backend has to accept each challenge once and keep the per-key counter moving forward.
290
+ - Mixing environments. Sandbox keys and receipts fail against production and the reverse.
291
+ - Regenerating on `invalidKey` without knowing why. Establish whether the key
292
+ was already attested, never attested, or rejected, and only replace it when
293
+ its state is known to be bad.
294
+
295
+ ## Review checklist
296
+
297
+ - [ ] A new `DCDevice` token per operation; none cached
298
+ - [ ] `isSupported` checked, with a fallback for devices and extension types that lack App Attest
299
+ - [ ] One key per account per device; `keyId` stored only for that account on that device
300
+ - [ ] Keychain save, load and delete all use the same account-scoped query
301
+ - [ ] One attestation per key; server keeps the public key and receipt
302
+ - [ ] Attestation verifier covers: certificates, App ID hash, `aaguid` for the environment, credential ID, and a nonce of `SHA256(authData || SHA256(challenge))`
303
+ - [ ] Each assertion signs a challenge plus request context; the backend checks the signature, RP ID, counter, the challenge and how it binds to the request
304
+ - [ ] Protected endpoints still require user authentication and authorization
305
+ - [ ] `DCError` handled: `.serverUnavailable` retried with same key and hash, bad keys discarded and regenerated
306
+ - [ ] Environment entitlement agrees with sandbox or production server routing
307
+ - [ ] Rollout gated behind a feature flag
429
308
 
430
309
  ## References
431
310
 
432
- - Extended patterns: [references/device-integrity-patterns.md](references/device-integrity-patterns.md)
433
- - [DeviceCheck framework](https://sosumi.ai/documentation/devicecheck)
434
- - [DCDevice](https://sosumi.ai/documentation/devicecheck/dcdevice)
435
- - [DCAppAttestService](https://sosumi.ai/documentation/devicecheck/dcappattestservice)
436
- - [Establishing your app's integrity](https://sosumi.ai/documentation/devicecheck/establishing-your-app-s-integrity)
437
- - [Validating apps that connect to your server](https://sosumi.ai/documentation/devicecheck/validating-apps-that-connect-to-your-server)
438
- - [Attestation Object Validation Guide](https://sosumi.ai/documentation/devicecheck/attestation-object-validation-guide)
439
- - [App Attest Environment](https://sosumi.ai/documentation/bundleresources/entitlements/com.apple.developer.devicecheck.appattest-environment)
311
+ - [Patterns reference](references/device-integrity-patterns.md) - server verification steps, architecture, retries, rejected keys, rollout
312
+ - Apple documentation: [DeviceCheck](https://developer.apple.com/documentation/devicecheck),
313
+ [DCDevice](https://developer.apple.com/documentation/devicecheck/dcdevice),
314
+ [DCAppAttestService](https://developer.apple.com/documentation/devicecheck/dcappattestservice),
315
+ plus the DeviceCheck articles on app integrity, server-side validation,
316
+ attestation objects and the environment entitlement (see the patterns reference)