@hasna/recordings 0.2.10 → 0.2.13

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 (167) hide show
  1. package/README.md +350 -35
  2. package/bun.lock +3 -0
  3. package/dist/__tests__/helpers/native-fs-guard.d.ts +2 -0
  4. package/dist/__tests__/helpers/native-fs-guard.d.ts.map +1 -0
  5. package/dist/cli/index.js +1728 -320
  6. package/dist/cli/macos-permissions.d.ts +13 -0
  7. package/dist/cli/macos-permissions.d.ts.map +1 -0
  8. package/dist/cli/options.d.ts +12 -0
  9. package/dist/cli/options.d.ts.map +1 -1
  10. package/dist/db/pg-migrations.d.ts.map +1 -1
  11. package/dist/db/recordings.d.ts +2 -1
  12. package/dist/db/recordings.d.ts.map +1 -1
  13. package/dist/db/remote-storage.d.ts +5 -1
  14. package/dist/db/remote-storage.d.ts.map +1 -1
  15. package/dist/index.d.ts +2 -2
  16. package/dist/index.d.ts.map +1 -1
  17. package/dist/index.js +628 -147
  18. package/dist/lib/bun-runtime.d.ts +11 -0
  19. package/dist/lib/bun-runtime.d.ts.map +1 -0
  20. package/dist/lib/config.d.ts +8 -1
  21. package/dist/lib/config.d.ts.map +1 -1
  22. package/dist/lib/enhancer.d.ts +10 -2
  23. package/dist/lib/enhancer.d.ts.map +1 -1
  24. package/dist/lib/install-maintenance.d.ts +24 -0
  25. package/dist/lib/install-maintenance.d.ts.map +1 -0
  26. package/dist/lib/machine.d.ts +2 -0
  27. package/dist/lib/machine.d.ts.map +1 -0
  28. package/dist/lib/recorder.d.ts +2 -1
  29. package/dist/lib/recorder.d.ts.map +1 -1
  30. package/dist/lib/recording-create-identity.d.ts +13 -0
  31. package/dist/lib/recording-create-identity.d.ts.map +1 -0
  32. package/dist/lib/release-install-policy.d.ts +31 -0
  33. package/dist/lib/release-install-policy.d.ts.map +1 -0
  34. package/dist/lib/transcriber.d.ts.map +1 -1
  35. package/dist/mcp/index.d.ts.map +1 -1
  36. package/dist/mcp/index.js +977 -194
  37. package/dist/sdk/index.js +5 -1
  38. package/dist/sdk/v1.generated.d.ts +4 -0
  39. package/dist/sdk/v1.generated.d.ts.map +1 -1
  40. package/dist/server/cloud-config.d.ts +9 -0
  41. package/dist/server/cloud-config.d.ts.map +1 -0
  42. package/dist/server/cloud-readiness.d.ts +10 -0
  43. package/dist/server/cloud-readiness.d.ts.map +1 -0
  44. package/dist/server/cloud.d.ts +9 -18
  45. package/dist/server/cloud.d.ts.map +1 -1
  46. package/dist/server/index.js +1654 -304
  47. package/dist/server/migrate-command.d.ts +11 -0
  48. package/dist/server/migrate-command.d.ts.map +1 -0
  49. package/dist/server/openapi.d.ts +22 -0
  50. package/dist/server/openapi.d.ts.map +1 -1
  51. package/dist/server/repo.d.ts +8 -1
  52. package/dist/server/repo.d.ts.map +1 -1
  53. package/dist/server/serve.d.ts +6 -1
  54. package/dist/server/serve.d.ts.map +1 -1
  55. package/dist/server/v1.d.ts.map +1 -1
  56. package/dist/storage.js +515 -80
  57. package/dist/store.d.ts +3 -1
  58. package/dist/store.d.ts.map +1 -1
  59. package/dist/types/index.d.ts +10 -0
  60. package/dist/types/index.d.ts.map +1 -1
  61. package/dist/version.d.ts +1 -1
  62. package/package.json +7 -4
  63. package/packaging/macos/Empty.entitlements +5 -0
  64. package/packaging/macos/Library/LaunchDaemons/com.hasna.recordings.updater.plist +34 -0
  65. package/packaging/macos/Verifier.entitlements +10 -0
  66. package/packaging/macos/artifact-verifier.sb +33 -0
  67. package/packaging/macos/build_release_pkg.sh +872 -0
  68. package/packaging/macos/managed_bootstrap.sh +623 -0
  69. package/packaging/macos/pkgutil_fingerprint.awk +37 -0
  70. package/packaging/macos/release_lifecycle.ts +463 -0
  71. package/packaging/macos/scripts/postinstall +565 -0
  72. package/packaging/macos/scripts/preinstall +297 -0
  73. package/scripts/build_companion_cli.sh +337 -0
  74. package/scripts/build_native_fs_guard.sh +59 -0
  75. package/scripts/generate-sdk.ts +19 -1
  76. package/scripts/install_macos_app.sh +1922 -77
  77. package/scripts/macos_artifact.ts +5316 -0
  78. package/scripts/migrate.ts +38 -10
  79. package/scripts/native/prebuilds/darwin-universal/recordings_fs_guard.node +0 -0
  80. package/scripts/native/recordings_fs_guard.c +1167 -0
  81. package/scripts/native_fs_guard.ts +158 -0
  82. package/scripts/release-guard.ts +20 -1
  83. package/scripts/resolve_tailscale_cli.sh +389 -0
  84. package/scripts/smoke_macos_app.sh +573 -0
  85. package/src/native/Recordings/App/ContentView.swift +83 -0
  86. package/src/native/Recordings/App/MenuBarStatusView.swift +102 -0
  87. package/src/native/Recordings/App/RecordWorkspaceView.swift +468 -0
  88. package/src/native/Recordings/App/RecordingDetailView.swift +138 -0
  89. package/src/native/Recordings/App/RecordingsApp.swift +367 -44
  90. package/src/native/Recordings/App/RecordingsListView.swift +130 -0
  91. package/src/native/Recordings/App/RecordingsStore.swift +206 -0
  92. package/src/native/Recordings/App/RuntimeSmoke.swift +158 -0
  93. package/src/native/Recordings/App/SidebarView.swift +212 -0
  94. package/src/native/Recordings/App/Theme.swift +87 -0
  95. package/src/native/Recordings/Package.resolved +5 -5
  96. package/src/native/Recordings/Package.swift +62 -2
  97. package/src/native/Recordings/RecordingsLib/AccessibilityPromptGate.swift +104 -0
  98. package/src/native/Recordings/RecordingsLib/BrandAssets.swift +0 -32
  99. package/src/native/Recordings/RecordingsLib/ChromeSurface.swift +17 -0
  100. package/src/native/Recordings/RecordingsLib/Info.plist +7 -3
  101. package/src/native/Recordings/RecordingsLib/MenuBarPresentation.swift +37 -0
  102. package/src/native/Recordings/RecordingsLib/NativeAppDiagnostics.swift +32 -2
  103. package/src/native/Recordings/RecordingsLib/NativeMachineIdentity.swift +24 -0
  104. package/src/native/Recordings/RecordingsLib/NativePCMRecorder.swift +262 -23
  105. package/src/native/Recordings/RecordingsLib/OpenAIAPIKeyStore.swift +56 -0
  106. package/src/native/Recordings/RecordingsLib/PermissionRequestLaunchPlan.swift +56 -0
  107. package/src/native/Recordings/RecordingsLib/ProjectStore.swift +321 -25
  108. package/src/native/Recordings/RecordingsLib/RealtimeTranscriptionClient.swift +605 -90
  109. package/src/native/Recordings/RecordingsLib/Recording.swift +141 -0
  110. package/src/native/Recordings/RecordingsLib/RecordingEngine.swift +3704 -376
  111. package/src/native/Recordings/RecordingsLib/RecordingStartControlPresentation.swift +29 -0
  112. package/src/native/Recordings/RecordingsLib/RecordingsCLI.entitlements +10 -0
  113. package/src/native/Recordings/RecordingsLib/RecordingsCLI.swift +222 -0
  114. package/src/native/Recordings/RecordingsLib/SettingsView.swift +45 -11
  115. package/src/native/Recordings/RecordingsLib/SpeechIntent.swift +331 -0
  116. package/src/native/Recordings/RecordingsLib/SpeechIntentClassifier.swift +232 -0
  117. package/src/native/Recordings/RecordingsLib/VoiceShortcuts.swift +17 -7
  118. package/src/native/Recordings/RecordingsTests/CLIRunnerTests.swift +1343 -0
  119. package/src/native/Recordings/RecordingsTests/MenuBarPresentationTests.swift +98 -0
  120. package/src/native/Recordings/RecordingsTests/NativeAppDiagnosticsTests.swift +20 -0
  121. package/src/native/Recordings/RecordingsTests/NativePCMRecorderTests.swift +370 -1
  122. package/src/native/Recordings/RecordingsTests/PasteTargetTests.swift +987 -1
  123. package/src/native/Recordings/RecordingsTests/ProjectStoreTests.swift +385 -0
  124. package/src/native/Recordings/RecordingsTests/RealtimeTranscriptionTests.swift +835 -8
  125. package/src/native/Recordings/RecordingsTests/RecordingBridgeTests.swift +180 -0
  126. package/src/native/Recordings/RecordingsTests/RecordingEngineDeliveryTests.swift +760 -0
  127. package/src/native/Recordings/RecordingsTests/RecordingStartControlPresentationTests.swift +42 -0
  128. package/src/native/Recordings/RecordingsTests/RecordingStartGateTests.swift +264 -0
  129. package/src/native/Recordings/RecordingsTests/RecordingStartTimingTests.swift +129 -0
  130. package/src/native/Recordings/RecordingsTests/SpeechIntentTests.swift +600 -0
  131. package/src/native/Recordings/RecordingsTests/TranscriptResolutionTests.swift +62 -1
  132. package/src/native/Recordings/RecordingsTests/TranscriptionResultIdentityTests.swift +21 -0
  133. package/src/native/Recordings/Updater/BootstrapPreflight/BootstrapPreflightMain.swift +739 -0
  134. package/src/native/Recordings/Updater/Broker/ActivationRecoveryPolicy.swift +254 -0
  135. package/src/native/Recordings/Updater/Broker/ApplicationNamespace.swift +463 -0
  136. package/src/native/Recordings/Updater/Broker/ApplicationProcessQuiescence.swift +55 -0
  137. package/src/native/Recordings/Updater/Broker/ArtifactIngest.swift +341 -0
  138. package/src/native/Recordings/Updater/Broker/AtomicActivation.swift +477 -0
  139. package/src/native/Recordings/Updater/Broker/BrokerMain.swift +624 -0
  140. package/src/native/Recordings/Updater/Broker/CanonicalTreeCopy.swift +63 -0
  141. package/src/native/Recordings/Updater/Broker/CodeValidation.swift +561 -0
  142. package/src/native/Recordings/Updater/Broker/DarwinACLValidator.swift +66 -0
  143. package/src/native/Recordings/Updater/Broker/HostOSProductVersion.swift +50 -0
  144. package/src/native/Recordings/Updater/Broker/InstallJournal.swift +362 -0
  145. package/src/native/Recordings/Updater/Broker/InstallRecovery.swift +662 -0
  146. package/src/native/Recordings/Updater/Broker/MonotonicState.swift +456 -0
  147. package/src/native/Recordings/Updater/Broker/PeerIdentity.swift +274 -0
  148. package/src/native/Recordings/Updater/Broker/VerifierRunner.swift +81 -0
  149. package/src/native/Recordings/Updater/BrokerTests/ActivationRecoveryPolicyTests.swift +259 -0
  150. package/src/native/Recordings/Updater/BrokerTests/CanonicalReleaseOrderTests.swift +21 -0
  151. package/src/native/Recordings/Updater/Client/ClientMain.swift +119 -0
  152. package/src/native/Recordings/Updater/Protocol/BoundedProcess.swift +159 -0
  153. package/src/native/Recordings/Updater/Protocol/CandidateMetadataPolicy.swift +214 -0
  154. package/src/native/Recordings/Updater/Protocol/HostOSVersionPolicy.swift +55 -0
  155. package/src/native/Recordings/Updater/Protocol/MonotonicReleasePolicy.swift +152 -0
  156. package/src/native/Recordings/Updater/Protocol/ReleaseEnvelope.swift +229 -0
  157. package/src/native/Recordings/Updater/Protocol/UpdateProtocol.swift +119 -0
  158. package/src/native/Recordings/Updater/ProtocolTests/BoundedProcessRunnerTests.swift +60 -0
  159. package/src/native/Recordings/Updater/ProtocolTests/CandidateMetadataPolicyTests.swift +168 -0
  160. package/src/native/Recordings/Updater/ProtocolTests/HostOSVersionPolicyTests.swift +62 -0
  161. package/src/native/Recordings/Updater/ProtocolTests/MonotonicReleasePolicyTests.swift +172 -0
  162. package/src/native/Recordings/Updater/ProtocolTests/ReleaseEnvelopeValidationTests.swift +65 -0
  163. package/src/native/Recordings/Updater/Signer/SignerMain.swift +210 -0
  164. package/src/native/Recordings/Updater/VerifierLauncher/RecordingsVerifierLauncher.c +725 -0
  165. package/src/native/Recordings/Updater/VerifierLauncher/include/RecordingsVerifierLauncher.h +33 -0
  166. package/src/native/Recordings/build.sh +1990 -29
  167. package/src/native/Recordings/RecordingsLib/MenuBarPopover.swift +0 -380
@@ -0,0 +1,872 @@
1
+ #!/bin/bash
2
+ # Assemble, sign, notarize, and verify the root-owned Recordings updater bootstrap PKG.
3
+
4
+ set -euo pipefail
5
+ umask 077
6
+
7
+ export PATH=/usr/bin:/bin:/usr/sbin:/sbin
8
+ export LC_ALL=C
9
+ export LANG=C
10
+ export TZ=UTC0
11
+
12
+ APP=""
13
+ ARTIFACT_BASENAME=""
14
+ BROKER=""
15
+ VERIFIER=""
16
+ PUBLIC_KEY=""
17
+ VERSION=""
18
+ SOURCE_SHA=""
19
+ EXPECTED_TEAM_ID=""
20
+ INSTALLER_IDENTITY=""
21
+ NOTARY_PROFILE=""
22
+ OUTPUT_DIR=""
23
+ BUN_EXECUTABLE=""
24
+ RELEASE_SEQUENCE=""
25
+ KEY_EPOCH=""
26
+ EXPIRES_AT_UTC=""
27
+ ENVELOPE_SIGNER=""
28
+ SOURCE_ROOT=""
29
+ ENVELOPE_PRIVATE_KEY=""
30
+ APP_ARCHIVE=""
31
+ MANIFEST=""
32
+ BOOTSTRAP_PREFLIGHT_VERIFIER=""
33
+ PACKAGE_STAGE_DIR=""
34
+ PACKAGE_FINAL_DIR=""
35
+ PACKAGE_RESERVATION=""
36
+ PACKAGE_RESERVATION_OWNED=0
37
+ PACKAGE_DIRECTORY_PUBLISHED=0
38
+ PUBLICATION_IDENTITY_SHA256=""
39
+ WORK_DIR=""
40
+
41
+ require_no_extended_acl() {
42
+ local path="$1"
43
+ local acl_listing acl_status
44
+ acl_listing="$(/bin/ls -lade "$path")" || {
45
+ echo "Package builder could not inspect extended ACLs on: $path" >&2
46
+ exit 1
47
+ }
48
+ if /usr/bin/printf '%s\n' "$acl_listing" | /usr/bin/awk '
49
+ NR == 1 && length($1) == 11 && substr($1, 11, 1) == "+" { found = 1 }
50
+ NR > 1 && $1 ~ /^[0-9]+:$/ { found = 1 }
51
+ END { exit found ? 0 : 1 }
52
+ '; then
53
+ echo "Package input trust path has an unexpected extended ACL: $path" >&2
54
+ exit 1
55
+ else
56
+ acl_status=$?
57
+ [ "$acl_status" -eq 1 ] || {
58
+ echo "Package builder could not evaluate extended ACLs on: $path" >&2
59
+ exit 1
60
+ }
61
+ fi
62
+ }
63
+
64
+ require_tree_without_extended_acl() {
65
+ local tree="$1"
66
+ local acl_listing acl_status
67
+ require_no_extended_acl "$tree"
68
+ [ -d "$tree" ] || return 0
69
+ acl_listing="$(/bin/ls -laeR "$tree")" || {
70
+ echo "Package builder could not recursively inspect extended ACLs on: $tree" >&2
71
+ exit 1
72
+ }
73
+ if /usr/bin/printf '%s\n' "$acl_listing" | /usr/bin/awk '
74
+ length($1) == 11 && substr($1, 11, 1) == "+" { found = 1 }
75
+ $1 ~ /^[0-9]+:$/ { found = 1 }
76
+ END { exit found ? 0 : 1 }
77
+ '; then
78
+ echo "Package input tree has an unexpected extended ACL: $tree" >&2
79
+ exit 1
80
+ else
81
+ acl_status=$?
82
+ [ "$acl_status" -eq 1 ] || {
83
+ echo "Package builder could not evaluate recursive extended ACLs on: $tree" >&2
84
+ exit 1
85
+ }
86
+ fi
87
+ }
88
+
89
+ require_exact_regular_file_mode() {
90
+ local path="$1"
91
+ local expected_mode="$2"
92
+ local label="$3"
93
+ local mode_match
94
+ [ -f "$path" ] && [ ! -L "$path" ] || {
95
+ echo "$label is missing, linked, or not a regular file: $path" >&2
96
+ exit 1
97
+ }
98
+ mode_match="$(/usr/bin/find "$path" -xdev -type f -links 1 -perm "$expected_mode" -print -quit)" || {
99
+ echo "Package builder could not inspect $label mode and link count: $path" >&2
100
+ exit 1
101
+ }
102
+ [ "$mode_match" = "$path" ] || {
103
+ echo "$label must be singly linked with exact mode $expected_mode: $path" >&2
104
+ exit 1
105
+ }
106
+ }
107
+
108
+ require_exact_binary_architectures() {
109
+ local binary="$1"
110
+ shift
111
+ local actual_architectures actual_architecture expected_architecture
112
+ local actual_count=0 expected_count="$#" matches
113
+ actual_architectures="$(/usr/bin/lipo -archs "$binary")" || {
114
+ echo "Package builder could not read binary architectures: $binary" >&2
115
+ exit 1
116
+ }
117
+ for actual_architecture in $actual_architectures; do
118
+ actual_count=$((actual_count + 1))
119
+ case " $* " in
120
+ *" $actual_architecture "*) ;;
121
+ *) echo "Package input contains unsupported architecture $actual_architecture: $binary" >&2; exit 1 ;;
122
+ esac
123
+ done
124
+ [ "$actual_count" -eq "$expected_count" ] || {
125
+ echo "Package input architecture count does not match release policy: $binary" >&2
126
+ exit 1
127
+ }
128
+ for expected_architecture in "$@"; do
129
+ matches=0
130
+ for actual_architecture in $actual_architectures; do
131
+ [ "$actual_architecture" = "$expected_architecture" ] && matches=$((matches + 1))
132
+ done
133
+ [ "$matches" -eq 1 ] || {
134
+ echo "Package input must contain exactly one $expected_architecture slice: $binary" >&2
135
+ exit 1
136
+ }
137
+ done
138
+ }
139
+
140
+ require_safe_signed_app_bundle_modes() {
141
+ local app="$1"
142
+ local unexpected hardlinked bad_mode directory
143
+ local main="$app/Contents/MacOS/Recordings"
144
+ local companion="$app/Contents/Helpers/recordings"
145
+ local client="$app/Contents/Helpers/recordings-update-client"
146
+
147
+ for directory in "$app" "$app/Contents" "$app/Contents/MacOS" "$app/Contents/Helpers"; do
148
+ [ -d "$directory" ] && [ ! -L "$directory" ] || {
149
+ echo "Signed app launch ancestry is missing, linked, or not a directory: $directory" >&2
150
+ exit 1
151
+ }
152
+ done
153
+ unexpected="$(/usr/bin/find "$app" -xdev ! -type d ! -type f -print -quit)" || {
154
+ echo "Package builder could not inspect the signed app tree structure." >&2
155
+ exit 1
156
+ }
157
+ [ -z "$unexpected" ] || {
158
+ echo "Signed app tree contains a symbolic link or special file: $unexpected" >&2
159
+ exit 1
160
+ }
161
+ hardlinked="$(/usr/bin/find "$app" -xdev -type f -links +1 -print -quit)" || {
162
+ echo "Package builder could not inspect signed app regular-file link counts." >&2
163
+ exit 1
164
+ }
165
+ [ -z "$hardlinked" ] || {
166
+ echo "Signed app tree contains a multiply-linked regular file: $hardlinked" >&2
167
+ exit 1
168
+ }
169
+ bad_mode="$(/usr/bin/find "$app" -xdev -type d ! -perm 0755 -print -quit)" || {
170
+ echo "Package builder could not inspect signed app directory modes." >&2
171
+ exit 1
172
+ }
173
+ [ -z "$bad_mode" ] || {
174
+ echo "Signed app directory must have exact mode 0755: $bad_mode" >&2
175
+ exit 1
176
+ }
177
+ require_exact_regular_file_mode "$main" 0755 "Signed app main executable"
178
+ require_exact_regular_file_mode "$companion" 0755 "Signed app companion helper"
179
+ require_exact_regular_file_mode "$client" 0755 "Signed app updater client"
180
+ bad_mode="$(/usr/bin/find "$app" -xdev -type f \
181
+ ! -path "$main" \
182
+ ! -path "$companion" \
183
+ ! -path "$client" \
184
+ ! -perm 0644 -print -quit)" || {
185
+ echo "Package builder could not inspect signed app data-file modes." >&2
186
+ exit 1
187
+ }
188
+ [ -z "$bad_mode" ] || {
189
+ echo "Signed app data file must have exact mode 0644: $bad_mode" >&2
190
+ exit 1
191
+ }
192
+ }
193
+
194
+ while [ "$#" -gt 0 ]; do
195
+ case "$1" in
196
+ --app) APP="${2:-}"; shift 2 ;;
197
+ --artifact-basename) ARTIFACT_BASENAME="${2:-}"; shift 2 ;;
198
+ --broker) BROKER="${2:-}"; shift 2 ;;
199
+ --verifier) VERIFIER="${2:-}"; shift 2 ;;
200
+ --public-key) PUBLIC_KEY="${2:-}"; shift 2 ;;
201
+ --version) VERSION="${2:-}"; shift 2 ;;
202
+ --source-sha) SOURCE_SHA="${2:-}"; shift 2 ;;
203
+ --team-id) EXPECTED_TEAM_ID="${2:-}"; shift 2 ;;
204
+ --installer-identity) INSTALLER_IDENTITY="${2:-}"; shift 2 ;;
205
+ --notary-profile) NOTARY_PROFILE="${2:-}"; shift 2 ;;
206
+ --output-dir) OUTPUT_DIR="${2:-}"; shift 2 ;;
207
+ --bun-executable) BUN_EXECUTABLE="${2:-}"; shift 2 ;;
208
+ --release-sequence) RELEASE_SEQUENCE="${2:-}"; shift 2 ;;
209
+ --key-epoch) KEY_EPOCH="${2:-}"; shift 2 ;;
210
+ --expires-at-utc) EXPIRES_AT_UTC="${2:-}"; shift 2 ;;
211
+ --publication-identity-sha256) PUBLICATION_IDENTITY_SHA256="${2:-}"; shift 2 ;;
212
+ --envelope-signer) ENVELOPE_SIGNER="${2:-}"; shift 2 ;;
213
+ --source-root) SOURCE_ROOT="${2:-}"; shift 2 ;;
214
+ --envelope-private-key) ENVELOPE_PRIVATE_KEY="${2:-}"; shift 2 ;;
215
+ --app-archive) APP_ARCHIVE="${2:-}"; shift 2 ;;
216
+ --manifest) MANIFEST="${2:-}"; shift 2 ;;
217
+ --bootstrap-preflight-verifier) BOOTSTRAP_PREFLIGHT_VERIFIER="${2:-}"; shift 2 ;;
218
+ *) echo "Unknown argument: $1" >&2; exit 2 ;;
219
+ esac
220
+ done
221
+
222
+ case "$VERSION" in
223
+ ''|*[!0-9A-Za-z._+-]*) echo "Package version is missing or invalid." >&2; exit 2 ;;
224
+ esac
225
+ [ "$ARTIFACT_BASENAME" = "Recordings-${VERSION}-macos-initial-bootstrap" ] || {
226
+ echo "Initial-bootstrap package artifact basename is missing or mode-incompatible." >&2
227
+ exit 2
228
+ }
229
+ [[ "$SOURCE_SHA" =~ ^[0-9a-f]{40}$ ]] || { echo "Package source SHA must be an exact commit." >&2; exit 2; }
230
+ [[ "$EXPECTED_TEAM_ID" =~ ^[A-Z0-9]{10}$ ]] || { echo "Package Team ID must be exact." >&2; exit 2; }
231
+ [ -n "$INSTALLER_IDENTITY" ] || { echo "Developer ID Installer identity is required." >&2; exit 2; }
232
+ [ -n "$NOTARY_PROFILE" ] || { echo "Notary keychain profile is required." >&2; exit 2; }
233
+ [[ "$RELEASE_SEQUENCE" =~ ^[1-9][0-9]*$ ]] || { echo "Release sequence must be a positive integer." >&2; exit 2; }
234
+ [[ "$KEY_EPOCH" =~ ^[1-9][0-9]*$ ]] || { echo "Key epoch must be a positive integer." >&2; exit 2; }
235
+ [ -n "$EXPIRES_AT_UTC" ] || { echo "Release-envelope expiry is required." >&2; exit 2; }
236
+ [[ "$PUBLICATION_IDENTITY_SHA256" =~ ^[a-f0-9]{64}$ ]] || {
237
+ echo "Package publication identity SHA-256 is missing or invalid." >&2
238
+ exit 2
239
+ }
240
+ case "$BUN_EXECUTABLE" in /*) ;; *) echo "Bun executable must be an explicit absolute path." >&2; exit 2 ;; esac
241
+ [ -f "$BUN_EXECUTABLE" ] && [ -x "$BUN_EXECUTABLE" ] || { echo "Bun executable is missing or unsafe." >&2; exit 1; }
242
+ case "$ENVELOPE_SIGNER" in /*) ;; *) echo "Envelope signer must be an absolute executable path." >&2; exit 2 ;; esac
243
+ [ -x "$ENVELOPE_SIGNER" ] || { echo "Envelope signer is missing or not executable." >&2; exit 1; }
244
+ case "$BOOTSTRAP_PREFLIGHT_VERIFIER" in
245
+ /*) ;;
246
+ *) echo "Bootstrap preflight verifier must be an absolute executable path." >&2; exit 2 ;;
247
+ esac
248
+ [ -x "$BOOTSTRAP_PREFLIGHT_VERIFIER" ] && [ ! -L "$BOOTSTRAP_PREFLIGHT_VERIFIER" ] || {
249
+ echo "Bootstrap preflight verifier is missing or unsafe." >&2
250
+ exit 1
251
+ }
252
+ case "$ENVELOPE_PRIVATE_KEY" in /*) ;; *) echo "Envelope private-key path must be absolute." >&2; exit 2 ;; esac
253
+ for release_input in "$APP_ARCHIVE" "$MANIFEST" "$ENVELOPE_PRIVATE_KEY"; do
254
+ [ -f "$release_input" ] && [ ! -L "$release_input" ] || { echo "Release input is missing or unsafe." >&2; exit 1; }
255
+ require_no_extended_acl "$release_input"
256
+ done
257
+ case "$OUTPUT_DIR" in
258
+ /*) ;;
259
+ *) echo "Package output directory must be absolute." >&2; exit 2 ;;
260
+ esac
261
+ case "$SOURCE_ROOT" in /*) ;; *) echo "Archived source root must be absolute." >&2; exit 2 ;; esac
262
+ [ -d "$SOURCE_ROOT" ] && [ ! -L "$SOURCE_ROOT" ] || { echo "Archived source root is missing or unsafe." >&2; exit 1; }
263
+ [ ! -e "$SOURCE_ROOT/.git" ] || { echo "Package inputs must come from an archived source tree, not a worktree." >&2; exit 1; }
264
+
265
+ SCRIPT_DIR="$(cd "$(/usr/bin/dirname "$0")" && /bin/pwd -P)"
266
+ [ "$SCRIPT_DIR" = "$(cd "$SOURCE_ROOT/packaging/macos" && /bin/pwd -P)" ] || {
267
+ echo "Package builder is not executing from the pinned archived source tree." >&2
268
+ exit 1
269
+ }
270
+ LAUNCHD_PLIST="$SCRIPT_DIR/Library/LaunchDaemons/com.hasna.recordings.updater.plist"
271
+ VERIFIER_SANDBOX="$SCRIPT_DIR/artifact-verifier.sb"
272
+ PKG_SCRIPTS="$SCRIPT_DIR/scripts"
273
+ PKGUTIL_FINGERPRINT_PARSER="$SCRIPT_DIR/pkgutil_fingerprint.awk"
274
+ CLIENT="$APP/Contents/Helpers/recordings-update-client"
275
+
276
+ for path in "$APP" "$BROKER" "$CLIENT" "$VERIFIER" "$BOOTSTRAP_PREFLIGHT_VERIFIER" "$PUBLIC_KEY" "$LAUNCHD_PLIST" "$VERIFIER_SANDBOX" "$PKG_SCRIPTS/preinstall" "$PKG_SCRIPTS/postinstall" "$PKGUTIL_FINGERPRINT_PARSER"; do
277
+ [ -e "$path" ] || { echo "Package input is missing: $path" >&2; exit 1; }
278
+ [ ! -L "$path" ] || { echo "Package input trust boundary cannot be a symbolic link: $path" >&2; exit 1; }
279
+ require_no_extended_acl "$path"
280
+ done
281
+ require_tree_without_extended_acl "$APP"
282
+ require_safe_signed_app_bundle_modes "$APP"
283
+ require_exact_regular_file_mode "$BROKER" 0755 "Signed updater broker"
284
+ require_exact_regular_file_mode "$VERIFIER" 0755 "Signed artifact verifier launcher"
285
+ require_exact_regular_file_mode "$BOOTSTRAP_PREFLIGHT_VERIFIER" 0755 "Signed bootstrap preflight verifier"
286
+ require_exact_binary_architectures "$APP/Contents/MacOS/Recordings" arm64 x86_64
287
+ require_exact_binary_architectures "$APP/Contents/Helpers/recordings" arm64 x86_64
288
+ require_exact_binary_architectures "$CLIENT" arm64 x86_64
289
+ require_exact_binary_architectures "$BROKER" arm64 x86_64
290
+ require_exact_binary_architectures "$BOOTSTRAP_PREFLIGHT_VERIFIER" arm64 x86_64
291
+ require_exact_binary_architectures "$VERIFIER" arm64 x86_64
292
+ [ "$(/usr/bin/stat -f '%z' "$PUBLIC_KEY")" = "32" ] || { echo "Ed25519 public key must be exactly 32 raw bytes." >&2; exit 1; }
293
+ [ "$(/usr/bin/stat -f '%z' "$ENVELOPE_PRIVATE_KEY")" = "32" ] || { echo "Ed25519 private key must be exactly 32 raw bytes." >&2; exit 1; }
294
+ [ "$(/usr/bin/stat -f '%u' "$ENVELOPE_PRIVATE_KEY")" = "$(/usr/bin/id -u)" ] && \
295
+ [ $((8#$(/usr/bin/stat -f '%Lp' "$ENVELOPE_PRIVATE_KEY") & 8#077)) -eq 0 ] || {
296
+ echo "Envelope private key must be owner-only and owned by the isolated builder." >&2
297
+ exit 1
298
+ }
299
+
300
+ ATTESTATION="/Library/Application Support/Hasna/Recordings/BuildTrust/isolated-builder-v1"
301
+ TRUSTED_BUILD_ROOT="/private/var/recordings-build"
302
+ CURRENT_UID="$(/usr/bin/id -u)"
303
+ [ "$(/usr/bin/id -un)" = "_recordingsbuild" ] || {
304
+ echo "Release PKGs may only be assembled by the isolated _recordingsbuild identity." >&2
305
+ exit 1
306
+ }
307
+ for trusted_path in "$ATTESTATION" "$TRUSTED_BUILD_ROOT"; do
308
+ [ -e "$trusted_path" ] && [ ! -L "$trusted_path" ] || {
309
+ echo "Managed isolated-builder trust path is missing or unsafe: $trusted_path" >&2
310
+ exit 1
311
+ }
312
+ require_no_extended_acl "$trusted_path"
313
+ done
314
+ [ -f "$ATTESTATION" ] && [ "$(/usr/bin/stat -f '%u' "$ATTESTATION")" = "0" ] || {
315
+ echo "Isolated-builder attestation is not a root-owned regular file." >&2
316
+ exit 1
317
+ }
318
+ case "$(/usr/bin/stat -f '%Lp' "$ATTESTATION")" in 400|440|444) ;; *) echo "Isolated-builder attestation mode is unsafe." >&2; exit 1 ;; esac
319
+ [ "$(/bin/cat "$ATTESTATION")" = "recordings-isolated-builder-v1" ] || {
320
+ echo "Isolated-builder attestation content is invalid." >&2
321
+ exit 1
322
+ }
323
+ [ -d "$TRUSTED_BUILD_ROOT" ] && [ "$(/usr/bin/stat -f '%u' "$TRUSTED_BUILD_ROOT")" = "$CURRENT_UID" ] && \
324
+ [ "$(/usr/bin/stat -f '%Lp' "$TRUSTED_BUILD_ROOT")" = "700" ] || {
325
+ echo "Managed release build root must be owned by _recordingsbuild with mode 0700." >&2
326
+ exit 1
327
+ }
328
+ TRUSTED_BUILD_PARENT="$(/usr/bin/dirname "$TRUSTED_BUILD_ROOT")"
329
+ [ "$(/usr/bin/stat -f '%u' "$TRUSTED_BUILD_PARENT")" = "0" ] || {
330
+ echo "Managed release build parent must be root-owned." >&2
331
+ exit 1
332
+ }
333
+ require_no_extended_acl "$TRUSTED_BUILD_PARENT"
334
+ case "$(/usr/bin/stat -f '%Lp' "$TRUSTED_BUILD_PARENT")" in
335
+ *[2367][0-9]|*[0-9][2367]) echo "Managed release build parent is group/other writable." >&2; exit 1 ;;
336
+ esac
337
+
338
+ case "$OUTPUT_DIR" in
339
+ "$TRUSTED_BUILD_ROOT"/*) ;;
340
+ *) echo "Package output directory must remain inside the managed release build root." >&2; exit 1 ;;
341
+ esac
342
+ [ "$(cd "$OUTPUT_DIR" && /bin/pwd -P)" = "$OUTPUT_DIR" ] || {
343
+ echo "Package output directory must be canonical and contain no linked ancestor." >&2
344
+ exit 1
345
+ }
346
+ [ -d "$OUTPUT_DIR" ] && [ ! -L "$OUTPUT_DIR" ] && \
347
+ [ "$(/usr/bin/stat -f '%u' "$OUTPUT_DIR")" = "$CURRENT_UID" ] && \
348
+ [ "$(/usr/bin/stat -f '%Lp' "$OUTPUT_DIR")" = "700" ] || {
349
+ echo "Package output directory must be an existing isolated-builder-owned 0700 directory." >&2
350
+ exit 1
351
+ }
352
+ require_no_extended_acl "$OUTPUT_DIR"
353
+ output_relative="${OUTPUT_DIR#"$TRUSTED_BUILD_ROOT"/}"
354
+ output_ancestor="$TRUSTED_BUILD_ROOT"
355
+ IFS='/' read -r -a output_components <<<"$output_relative"
356
+ for output_component in "${output_components[@]}"; do
357
+ output_ancestor="$output_ancestor/$output_component"
358
+ [ -d "$output_ancestor" ] && [ ! -L "$output_ancestor" ] && \
359
+ [ "$(/usr/bin/stat -f '%u' "$output_ancestor")" = "$CURRENT_UID" ] && \
360
+ [ "$(/usr/bin/stat -f '%Lp' "$output_ancestor")" = "700" ] || {
361
+ echo "Package output ancestry must remain isolated-builder-owned mode 0700: $output_ancestor" >&2
362
+ exit 1
363
+ }
364
+ require_no_extended_acl "$output_ancestor"
365
+ done
366
+
367
+ WORK_DIR="$(/usr/bin/mktemp -d "$TRUSTED_BUILD_ROOT/recordings-release-pkg.XXXXXX")"
368
+ /bin/chmod -N "$WORK_DIR"
369
+ /bin/chmod 0700 "$WORK_DIR"
370
+ require_no_extended_acl "$WORK_DIR"
371
+ cleanup() {
372
+ local status=$?
373
+ trap - EXIT
374
+ /bin/rm -rf "$WORK_DIR"
375
+ if [ "$PACKAGE_DIRECTORY_PUBLISHED" -eq 0 ] && [ -n "$PACKAGE_STAGE_DIR" ]; then
376
+ /bin/rm -rf "$PACKAGE_STAGE_DIR"
377
+ fi
378
+ if [ "$PACKAGE_RESERVATION_OWNED" -eq 1 ] && \
379
+ [ ! -e "$PACKAGE_FINAL_DIR" ] && [ ! -L "$PACKAGE_FINAL_DIR" ]; then
380
+ /bin/rm -f "$PACKAGE_RESERVATION/.recordings-publication.json" 2>/dev/null || true
381
+ /bin/rmdir "$PACKAGE_RESERVATION" 2>/dev/null || true
382
+ fi
383
+ exit "$status"
384
+ }
385
+ trap cleanup EXIT
386
+
387
+ sha256_file() {
388
+ /usr/bin/shasum -a 256 "$1" | /usr/bin/awk '{ print $1 }'
389
+ }
390
+
391
+ verify_developer_id_application() {
392
+ local code_path="$1"
393
+ local expected_identifier="${2:-}"
394
+ local details authority team identifier timestamp
395
+ /usr/bin/codesign --verify --strict --all-architectures --verbose=2 "$code_path"
396
+ details="$(/usr/bin/codesign -d --verbose=4 "$code_path" 2>&1)"
397
+ authority="$(printf '%s\n' "$details" | /usr/bin/awk -F= '/^Authority=/ { print $2; exit }')"
398
+ team="$(printf '%s\n' "$details" | /usr/bin/awk -F= '/^TeamIdentifier=/ { print $2; exit }')"
399
+ identifier="$(printf '%s\n' "$details" | /usr/bin/awk -F= '/^Identifier=/ { print $2; exit }')"
400
+ timestamp="$(printf '%s\n' "$details" | /usr/bin/awk -F= '/^Timestamp=/ { print $2; exit }')"
401
+ [[ "$authority" == "Developer ID Application:"* ]] || { echo "$code_path is not Developer ID Application signed." >&2; exit 1; }
402
+ [ "$team" = "$EXPECTED_TEAM_ID" ] || { echo "$code_path has the wrong TeamIdentifier." >&2; exit 1; }
403
+ [ -z "$expected_identifier" ] || [ "$identifier" = "$expected_identifier" ] || {
404
+ echo "$code_path has the wrong signing identifier." >&2
405
+ exit 1
406
+ }
407
+ case "$timestamp" in ''|none|None|NONE) echo "$code_path has no trusted timestamp." >&2; exit 1 ;; esac
408
+ [[ "$details" == *"flags="*"runtime"* ]] || { echo "$code_path lacks hardened runtime." >&2; exit 1; }
409
+ }
410
+
411
+ verify_developer_id_application "$BROKER" "com.hasna.recordings.updater"
412
+ verify_developer_id_application "$CLIENT" "com.hasna.recordings.update-client"
413
+ verify_developer_id_application "$VERIFIER" "com.hasna.recordings.artifact-verifier"
414
+ verify_developer_id_application "$BOOTSTRAP_PREFLIGHT_VERIFIER" "com.hasna.recordings.bootstrap-preflight"
415
+ /usr/bin/codesign --verify --deep --strict --all-architectures --verbose=2 "$APP"
416
+
417
+ PUBLIC_KEY_SNAPSHOT="$WORK_DIR/release-envelope-public.raw"
418
+ PUBLIC_KEY_DIGEST="$("$VERIFIER" snapshot-regular-file \
419
+ --source "$PUBLIC_KEY" \
420
+ --destination "$PUBLIC_KEY_SNAPSHOT" \
421
+ --maximum-bytes 32 \
422
+ --expected-bytes 32)"
423
+ PUBLIC_KEY="$PUBLIC_KEY_SNAPSHOT"
424
+
425
+ COMPUTED_PUBLICATION_IDENTITY_SHA256="$("$VERIFIER" release-publication-identity \
426
+ --component "release_kind=initial-bootstrap-updater" \
427
+ --component "source_sha=$SOURCE_SHA" \
428
+ --component "version=$VERSION" \
429
+ --component "team_id=$EXPECTED_TEAM_ID" \
430
+ --component "installer_identity=$INSTALLER_IDENTITY" \
431
+ --component "notary_profile=$NOTARY_PROFILE" \
432
+ --component "release_sequence=$RELEASE_SEQUENCE" \
433
+ --component "key_epoch=$KEY_EPOCH" \
434
+ --component "expires_at_utc=$EXPIRES_AT_UTC" \
435
+ --component "app_archive_sha256=$(sha256_file "$APP_ARCHIVE")" \
436
+ --component "manifest_sha256=$(sha256_file "$MANIFEST")" \
437
+ --component "broker_sha256=$(sha256_file "$BROKER")" \
438
+ --component "artifact_verifier_sha256=$(sha256_file "$VERIFIER")" \
439
+ --component "bootstrap_preflight_sha256=$(sha256_file "$BOOTSTRAP_PREFLIGHT_VERIFIER")" \
440
+ --component "envelope_public_key_sha256=$(sha256_file "$PUBLIC_KEY")" \
441
+ --component "envelope_signer_sha256=$(sha256_file "$ENVELOPE_SIGNER")")"
442
+ [ "$COMPUTED_PUBLICATION_IDENTITY_SHA256" = "$PUBLICATION_IDENTITY_SHA256" ] || {
443
+ echo "Package publication identity does not match the exact release inputs." >&2
444
+ exit 1
445
+ }
446
+
447
+ PACKAGE_SET_BASENAME="${ARTIFACT_BASENAME}-updater"
448
+ PKG_LEAF="${PACKAGE_SET_BASENAME}.pkg"
449
+ NOTARY_SUBMISSION_LEAF="${PACKAGE_SET_BASENAME}.notary-submit.json"
450
+ NOTARY_LOG_LEAF="${PACKAGE_SET_BASENAME}.notary-log.json"
451
+ PKG_SHA256_LEAF="${PACKAGE_SET_BASENAME}.pkg.sha256"
452
+ BOOTSTRAP_ENVELOPE_LEAF="${PACKAGE_SET_BASENAME}.bootstrap-envelope.json"
453
+ COMPATIBLE_COHORT_LEAF="${PACKAGE_SET_BASENAME}.compatible-cohort.json"
454
+ COMPATIBLE_COHORT_SHA256_LEAF="${COMPATIBLE_COHORT_LEAF}.sha256"
455
+ PACKAGE_FINAL_DIR="$OUTPUT_DIR/${PACKAGE_SET_BASENAME}.release"
456
+ PACKAGE_RESERVATION="$OUTPUT_DIR/.${PACKAGE_SET_BASENAME}.reservation"
457
+
458
+ if [ -e "$PACKAGE_FINAL_DIR" ] || [ -L "$PACKAGE_FINAL_DIR" ]; then
459
+ "$VERIFIER" complete-release-publication \
460
+ --destination "$PACKAGE_FINAL_DIR" \
461
+ --reservation "$PACKAGE_RESERVATION" \
462
+ --output-root "$OUTPUT_DIR" \
463
+ --publication-identity-sha256 "$PUBLICATION_IDENTITY_SHA256"
464
+ "$VERIFIER" assert-release-publication-complete \
465
+ --destination "$PACKAGE_FINAL_DIR" \
466
+ --output-root "$OUTPUT_DIR" \
467
+ --publication-identity-sha256 "$PUBLICATION_IDENTITY_SHA256"
468
+ printf 'Recovered authenticated same-version updater publication: %s\n' "$PACKAGE_FINAL_DIR"
469
+ exit 0
470
+ fi
471
+
472
+ for reserved_output in \
473
+ "$PACKAGE_FINAL_DIR" \
474
+ "$OUTPUT_DIR/$PKG_LEAF" \
475
+ "$OUTPUT_DIR/$NOTARY_SUBMISSION_LEAF" \
476
+ "$OUTPUT_DIR/$NOTARY_LOG_LEAF" \
477
+ "$OUTPUT_DIR/$PKG_SHA256_LEAF" \
478
+ "$OUTPUT_DIR/$BOOTSTRAP_ENVELOPE_LEAF" \
479
+ "$OUTPUT_DIR/$COMPATIBLE_COHORT_LEAF" \
480
+ "$OUTPUT_DIR/$COMPATIBLE_COHORT_SHA256_LEAF"; do
481
+ [ ! -e "$reserved_output" ] && [ ! -L "$reserved_output" ] || {
482
+ echo "Package release output already exists; same-version publication is immutable: $reserved_output" >&2
483
+ exit 1
484
+ }
485
+ done
486
+ if ! /bin/mkdir -m 0700 "$PACKAGE_RESERVATION"; then
487
+ echo "Package release output is already reserved by another or interrupted builder." >&2
488
+ exit 1
489
+ fi
490
+ PACKAGE_RESERVATION_OWNED=1
491
+ require_no_extended_acl "$PACKAGE_RESERVATION"
492
+
493
+ # Recheck after taking the exclusive reservation so a builder that completed
494
+ # between the initial probe and mkdir cannot be replaced.
495
+ for reserved_output in \
496
+ "$PACKAGE_FINAL_DIR" \
497
+ "$OUTPUT_DIR/$PKG_LEAF" \
498
+ "$OUTPUT_DIR/$NOTARY_SUBMISSION_LEAF" \
499
+ "$OUTPUT_DIR/$NOTARY_LOG_LEAF" \
500
+ "$OUTPUT_DIR/$PKG_SHA256_LEAF" \
501
+ "$OUTPUT_DIR/$BOOTSTRAP_ENVELOPE_LEAF" \
502
+ "$OUTPUT_DIR/$COMPATIBLE_COHORT_LEAF" \
503
+ "$OUTPUT_DIR/$COMPATIBLE_COHORT_SHA256_LEAF"; do
504
+ [ ! -e "$reserved_output" ] && [ ! -L "$reserved_output" ] || {
505
+ echo "Package release output appeared while acquiring its reservation." >&2
506
+ exit 1
507
+ }
508
+ done
509
+ PACKAGE_STAGE_DIR="$(/usr/bin/mktemp -d "$OUTPUT_DIR/.${PACKAGE_SET_BASENAME}.staging.XXXXXX")"
510
+ /bin/chmod -N "$PACKAGE_STAGE_DIR"
511
+ /bin/chmod 0700 "$PACKAGE_STAGE_DIR"
512
+ require_no_extended_acl "$PACKAGE_STAGE_DIR"
513
+
514
+ ROOT="$WORK_DIR/root"
515
+ SCRIPTS="$WORK_DIR/scripts"
516
+ PACKAGE_BOOTSTRAP_ID="$(/usr/bin/uuidgen | /usr/bin/tr '[:upper:]' '[:lower:]')"
517
+ [[ "$PACKAGE_BOOTSTRAP_ID" =~ ^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$ ]] || {
518
+ echo "Package builder could not create a valid bootstrap identity." >&2
519
+ exit 1
520
+ }
521
+ /bin/mkdir -p \
522
+ "$ROOT/Applications" \
523
+ "$ROOT/Library/PrivilegedHelperTools" \
524
+ "$ROOT/Library/LaunchDaemons" \
525
+ "$ROOT/Library/Application Support/Hasna/Recordings/Trust/envelope-keys" \
526
+ "$ROOT/Library/Application Support/Hasna/Recordings/Bootstrap" \
527
+ "$SCRIPTS"
528
+ require_no_extended_acl "$PACKAGE_STAGE_DIR"
529
+
530
+ /usr/bin/ditto "$APP" "$ROOT/Applications/Recordings.app"
531
+ /bin/cp "$BROKER" "$ROOT/Library/PrivilegedHelperTools/com.hasna.recordings.updater"
532
+ /bin/cp "$VERIFIER" "$ROOT/Library/PrivilegedHelperTools/com.hasna.recordings.artifact-verifier"
533
+ /bin/cp "$PUBLIC_KEY" "$ROOT/Library/Application Support/Hasna/Recordings/Trust/envelope-keys/${KEY_EPOCH}.raw"
534
+ /bin/cp "$LAUNCHD_PLIST" "$ROOT/Library/LaunchDaemons/com.hasna.recordings.updater.plist"
535
+ /bin/cp "$VERIFIER_SANDBOX" "$ROOT/Library/Application Support/Hasna/Recordings/Trust/artifact-verifier.sb"
536
+ for package_script_name in preinstall postinstall; do
537
+ package_script_source="$PKG_SCRIPTS/$package_script_name"
538
+ package_script_target="$SCRIPTS/$package_script_name"
539
+ [ "$(/usr/bin/grep -c '__RECORDINGS_PACKAGE_BOOTSTRAP_ID__' "$package_script_source")" = "1" ] || {
540
+ echo "Package $package_script_name must contain exactly one bootstrap-identity placeholder." >&2
541
+ exit 1
542
+ }
543
+ /usr/bin/sed "s/__RECORDINGS_PACKAGE_BOOTSTRAP_ID__/$PACKAGE_BOOTSTRAP_ID/" \
544
+ "$package_script_source" >"$package_script_target"
545
+ ! /usr/bin/grep -F '__RECORDINGS_PACKAGE_BOOTSTRAP_ID__' "$package_script_target" >/dev/null || {
546
+ echo "Package $package_script_name retained an unresolved bootstrap identity." >&2
547
+ exit 1
548
+ }
549
+ /usr/bin/grep -F "PACKAGE_BOOTSTRAP_ID=\"$PACKAGE_BOOTSTRAP_ID\"" \
550
+ "$package_script_target" >/dev/null || {
551
+ echo "Package $package_script_name did not bind the generated bootstrap identity." >&2
552
+ exit 1
553
+ }
554
+ done
555
+ /bin/cp "$BOOTSTRAP_PREFLIGHT_VERIFIER" "$SCRIPTS/recordings-bootstrap-preflight"
556
+ /usr/bin/printf '%s\n' "$RELEASE_SEQUENCE" >"$ROOT/Library/Application Support/Hasna/Recordings/Bootstrap/release-sequence"
557
+ /usr/bin/printf '%s\n' "$KEY_EPOCH" >"$ROOT/Library/Application Support/Hasna/Recordings/Bootstrap/key-epoch"
558
+ /usr/bin/printf '%s\n' "$SOURCE_SHA" >"$ROOT/Library/Application Support/Hasna/Recordings/Bootstrap/source-sha"
559
+ /usr/bin/printf '{"schema_version":1,"signing_team_identifier":"%s","allowed_client_identifiers":["com.hasna.recordings.update-client"],"application_identifier":"com.hasna.recordings","initial_key_epoch":%s,"allowed_key_epochs":[%s],"lifecycle":"bootstrap-v1-app-updates-only","root_maintenance_supported":false,"key_rotation_supported":false}\n' \
560
+ "$EXPECTED_TEAM_ID" "$KEY_EPOCH" "$KEY_EPOCH" \
561
+ >"$ROOT/Library/Application Support/Hasna/Recordings/Trust/broker-policy.json"
562
+ /bin/chmod -RN "$ROOT" "$SCRIPTS"
563
+ require_tree_without_extended_acl "$ROOT"
564
+ require_tree_without_extended_acl "$SCRIPTS"
565
+ /bin/chmod 0555 \
566
+ "$ROOT/Library/PrivilegedHelperTools/com.hasna.recordings.updater" \
567
+ "$ROOT/Library/PrivilegedHelperTools/com.hasna.recordings.artifact-verifier" \
568
+ "$SCRIPTS/preinstall" "$SCRIPTS/postinstall" "$SCRIPTS/recordings-bootstrap-preflight"
569
+ /bin/chmod 0444 \
570
+ "$ROOT/Library/Application Support/Hasna/Recordings/Trust/envelope-keys/${KEY_EPOCH}.raw" \
571
+ "$ROOT/Library/Application Support/Hasna/Recordings/Trust/broker-policy.json" \
572
+ "$ROOT/Library/Application Support/Hasna/Recordings/Trust/artifact-verifier.sb" \
573
+ "$ROOT/Library/Application Support/Hasna/Recordings/Bootstrap/release-sequence" \
574
+ "$ROOT/Library/Application Support/Hasna/Recordings/Bootstrap/key-epoch" \
575
+ "$ROOT/Library/Application Support/Hasna/Recordings/Bootstrap/source-sha" \
576
+ "$ROOT/Library/LaunchDaemons/com.hasna.recordings.updater.plist"
577
+ /usr/bin/plutil -lint "$ROOT/Library/LaunchDaemons/com.hasna.recordings.updater.plist" >/dev/null
578
+
579
+ STAGED_APP="$ROOT/Applications/Recordings.app"
580
+ STAGED_BROKER="$ROOT/Library/PrivilegedHelperTools/com.hasna.recordings.updater"
581
+ STAGED_CLIENT="$STAGED_APP/Contents/Helpers/recordings-update-client"
582
+ STAGED_VERIFIER="$ROOT/Library/PrivilegedHelperTools/com.hasna.recordings.artifact-verifier"
583
+ STAGED_BOOTSTRAP_PREFLIGHT="$SCRIPTS/recordings-bootstrap-preflight"
584
+ require_safe_signed_app_bundle_modes "$STAGED_APP"
585
+ require_exact_regular_file_mode "$STAGED_BROKER" 0555 "Staged updater broker"
586
+ require_exact_regular_file_mode "$STAGED_VERIFIER" 0555 "Staged artifact verifier launcher"
587
+ require_exact_regular_file_mode "$STAGED_BOOTSTRAP_PREFLIGHT" 0555 "Staged bootstrap preflight verifier"
588
+ require_exact_binary_architectures "$STAGED_APP/Contents/MacOS/Recordings" arm64 x86_64
589
+ require_exact_binary_architectures "$STAGED_APP/Contents/Helpers/recordings" arm64 x86_64
590
+ require_exact_binary_architectures "$STAGED_CLIENT" arm64 x86_64
591
+ require_exact_binary_architectures "$STAGED_BROKER" arm64 x86_64
592
+ require_exact_binary_architectures "$STAGED_BOOTSTRAP_PREFLIGHT" arm64 x86_64
593
+ require_exact_binary_architectures "$STAGED_VERIFIER" arm64 x86_64
594
+ verify_developer_id_application "$STAGED_BROKER" "com.hasna.recordings.updater"
595
+ verify_developer_id_application "$STAGED_CLIENT" "com.hasna.recordings.update-client"
596
+ verify_developer_id_application "$STAGED_VERIFIER" "com.hasna.recordings.artifact-verifier"
597
+ verify_developer_id_application "$STAGED_BOOTSTRAP_PREFLIGHT" "com.hasna.recordings.bootstrap-preflight"
598
+ /usr/bin/codesign --verify --deep --strict --all-architectures --verbose=2 "$STAGED_APP"
599
+ PROVENANCE="$STAGED_APP/Contents/Resources/recordings-build-provenance.json"
600
+ [ -f "$PROVENANCE" ] && [ ! -L "$PROVENANCE" ] || { echo "Signed app provenance is missing." >&2; exit 1; }
601
+ [ "$(/usr/bin/plutil -extract git_sha raw -o - "$PROVENANCE")" = "$SOURCE_SHA" ] || {
602
+ echo "Signed app provenance does not bind the package source commit." >&2
603
+ exit 1
604
+ }
605
+ [ "$(/usr/bin/plutil -extract team_id raw -o - "$PROVENANCE")" = "$EXPECTED_TEAM_ID" ] || {
606
+ echo "Signed app provenance does not bind the package TeamIdentifier." >&2
607
+ exit 1
608
+ }
609
+ RELEASE_ID="$(/usr/bin/uuidgen | /usr/bin/tr '[:upper:]' '[:lower:]')"
610
+ STAGED_CLIENT_SHA256="$(/usr/bin/shasum -a 256 "$STAGED_CLIENT" | /usr/bin/awk '{ print $1 }')"
611
+ STAGED_BROKER_SHA256="$(/usr/bin/shasum -a 256 "$STAGED_BROKER" | /usr/bin/awk '{ print $1 }')"
612
+ STAGED_VERIFIER_SHA256="$(/usr/bin/shasum -a 256 "$STAGED_VERIFIER" | /usr/bin/awk '{ print $1 }')"
613
+ STAGED_APP_TREE_SHA256="$(/usr/bin/plutil -extract binding.bundle_tree_sha256 raw -o - "$MANIFEST")"
614
+ ACTUAL_STAGED_APP_TREE_SHA256="$("$STAGED_VERIFIER" tree-digest --path "$STAGED_APP")"
615
+ [ "$ACTUAL_STAGED_APP_TREE_SHA256" = "$STAGED_APP_TREE_SHA256" ] || {
616
+ echo "Staged PKG application tree does not match the finalized manifest binding." >&2
617
+ exit 1
618
+ }
619
+ BOOTSTRAP_MARKER="$ROOT/Library/Application Support/Hasna/Recordings/Trust/bootstrap-marker.json"
620
+ /usr/bin/printf '{"schema_version":1,"key_epoch":%s,"release_sequence":%s,"release_id":"%s","version":"%s","source_commit":"%s","signing_team_identifier":"%s","app_tree_sha256":"%s","update_client_sha256":"%s","update_broker_sha256":"%s","artifact_verifier_sha256":"%s","lifecycle":"bootstrap-v1-app-updates-only","root_maintenance_supported":false,"key_rotation_supported":false}\n' \
621
+ "$KEY_EPOCH" "$RELEASE_SEQUENCE" "$RELEASE_ID" "$VERSION" "$SOURCE_SHA" "$EXPECTED_TEAM_ID" \
622
+ "$STAGED_APP_TREE_SHA256" "$STAGED_CLIENT_SHA256" "$STAGED_BROKER_SHA256" "$STAGED_VERIFIER_SHA256" \
623
+ >"$BOOTSTRAP_MARKER"
624
+ /bin/chmod 0444 "$BOOTSTRAP_MARKER"
625
+ /usr/bin/plutil -lint "$BOOTSTRAP_MARKER" >/dev/null
626
+ BOOTSTRAP_MARKER_SHA256="$(/usr/bin/shasum -a 256 "$BOOTSTRAP_MARKER" | /usr/bin/awk '{ print $1 }')"
627
+
628
+ PKG="$PACKAGE_STAGE_DIR/$PKG_LEAF"
629
+ NOTARY_SUBMISSION="$PACKAGE_STAGE_DIR/$NOTARY_SUBMISSION_LEAF"
630
+ NOTARY_LOG="$PACKAGE_STAGE_DIR/$NOTARY_LOG_LEAF"
631
+ PKG_SHA256="$PACKAGE_STAGE_DIR/$PKG_SHA256_LEAF"
632
+ ENVELOPE_PAYLOAD="$WORK_DIR/bootstrap-envelope-payload.json"
633
+ BOOTSTRAP_ENVELOPE="$PACKAGE_STAGE_DIR/$BOOTSTRAP_ENVELOPE_LEAF"
634
+ COMPATIBLE_COHORT="$PACKAGE_STAGE_DIR/$COMPATIBLE_COHORT_LEAF"
635
+ COMPATIBLE_COHORT_SHA256="$PACKAGE_STAGE_DIR/$COMPATIBLE_COHORT_SHA256_LEAF"
636
+
637
+ require_tree_without_extended_acl "$ROOT"
638
+ require_tree_without_extended_acl "$SCRIPTS"
639
+ require_no_extended_acl "$PACKAGE_STAGE_DIR"
640
+ /usr/bin/pkgbuild \
641
+ --root "$ROOT" \
642
+ --scripts "$SCRIPTS" \
643
+ --identifier com.hasna.recordings.updater \
644
+ --version "$VERSION" \
645
+ --install-location / \
646
+ --ownership recommended \
647
+ --timestamp \
648
+ --sign "$INSTALLER_IDENTITY" \
649
+ "$PKG"
650
+
651
+ verify_pkg_signature() {
652
+ local signature_output
653
+ signature_output="$(/usr/sbin/pkgutil --check-signature "$PKG")"
654
+ /usr/bin/grep -F "Status: signed by a certificate trusted by macOS" <<<"$signature_output" >/dev/null || \
655
+ /usr/bin/grep -F "Status: signed by a certificate trusted by Mac OS X" <<<"$signature_output" >/dev/null
656
+ /usr/bin/grep -E "^[[:space:]]*1\\. Developer ID Installer: .+ \\(${EXPECTED_TEAM_ID}\\)$" <<<"$signature_output" >/dev/null
657
+ }
658
+ verify_pkg_signature
659
+ SUBMITTED_PKG_SHA256="$(sha256_file "$PKG")"
660
+ /usr/bin/xcrun notarytool submit "$PKG" \
661
+ --keychain-profile "$NOTARY_PROFILE" \
662
+ --wait --output-format json >"$NOTARY_SUBMISSION"
663
+ [ "$(/usr/bin/plutil -extract status raw -o - "$NOTARY_SUBMISSION")" = "Accepted" ] || {
664
+ echo "Updater PKG notarization was not accepted." >&2
665
+ exit 1
666
+ }
667
+ NOTARY_ID="$(/usr/bin/plutil -extract id raw -o - "$NOTARY_SUBMISSION")"
668
+ [ -n "$NOTARY_ID" ] || { echo "Updater PKG notarization omitted its submission ID." >&2; exit 1; }
669
+ /usr/bin/xcrun notarytool log "$NOTARY_ID" --keychain-profile "$NOTARY_PROFILE" >"$NOTARY_LOG"
670
+ "$STAGED_VERIFIER" assert-notary-log \
671
+ --notary-log "$NOTARY_LOG" \
672
+ --submission-id "$NOTARY_ID" \
673
+ --submitted-archive-sha256 "$SUBMITTED_PKG_SHA256" || {
674
+ echo "Updater PKG notarization log is not accepted, issue-free, or byte-bound." >&2
675
+ exit 1
676
+ }
677
+ NOTARY_ARCHIVE_SHA256="$SUBMITTED_PKG_SHA256"
678
+ /usr/bin/xcrun stapler staple "$PKG"
679
+ /usr/bin/xcrun stapler validate "$PKG"
680
+ /usr/sbin/spctl --assess --type install --verbose=2 "$PKG"
681
+ verify_pkg_signature
682
+ PKG_PUBLISHED_SHA256="$(sha256_file "$PKG")"
683
+ /usr/bin/printf '%s %s\n' "$PKG_PUBLISHED_SHA256" "$PKG_LEAF" >"$PKG_SHA256"
684
+
685
+ designated_requirement() {
686
+ local requirement
687
+ requirement="$(/usr/bin/codesign -d -r- "$1" 2>&1 | /usr/bin/sed -n 's/^designated => //p' | /usr/bin/head -n 1)"
688
+ [ -n "$requirement" ] || { echo "Signed component is missing its designated requirement." >&2; exit 1; }
689
+ printf '%s\n' "$requirement"
690
+ }
691
+
692
+ APP_ARCHIVE_SHA256="$(sha256_file "$APP_ARCHIVE")"
693
+ MANIFEST_SHA256="$(sha256_file "$MANIFEST")"
694
+ PKG_DIGEST="$(sha256_file "$PKG")"
695
+ CLIENT_DIGEST="$(sha256_file "$STAGED_CLIENT")"
696
+ BROKER_DIGEST="$(sha256_file "$STAGED_BROKER")"
697
+ VERIFIER_DIGEST="$(sha256_file "$STAGED_VERIFIER")"
698
+ PUBLIC_KEY_DIGEST="$(sha256_file "$PUBLIC_KEY")"
699
+ APP_REQUIREMENT="$(designated_requirement "$STAGED_APP")"
700
+ CLIENT_REQUIREMENT="$(designated_requirement "$STAGED_CLIENT")"
701
+ BROKER_REQUIREMENT="$(designated_requirement "$STAGED_BROKER")"
702
+ VERIFIER_REQUIREMENT="$(designated_requirement "$STAGED_VERIFIER")"
703
+ [ "$(/usr/bin/plutil -extract archive.sha256 raw -o - "$MANIFEST")" = "$APP_ARCHIVE_SHA256" ] || {
704
+ echo "Final app manifest does not bind the release archive." >&2
705
+ exit 1
706
+ }
707
+ [ "$(/usr/bin/plutil -extract git_sha raw -o - "$MANIFEST")" = "$SOURCE_SHA" ] || {
708
+ echo "Final app manifest does not bind the source commit." >&2
709
+ exit 1
710
+ }
711
+ [ "$(/usr/bin/plutil -extract team_id raw -o - "$MANIFEST")" = "$EXPECTED_TEAM_ID" ] || {
712
+ echo "Final app manifest does not bind the TeamIdentifier." >&2
713
+ exit 1
714
+ }
715
+ [ "$(/usr/bin/plutil -extract bundle_version raw -o - "$MANIFEST")" = "$VERSION" ] || {
716
+ echo "Final app manifest does not bind the package version." >&2
717
+ exit 1
718
+ }
719
+ MANIFEST_ARCHITECTURES="$(/usr/bin/plutil -extract architectures json -o - "$MANIFEST" | /usr/bin/tr -d '[:space:]')"
720
+ [ "$MANIFEST_ARCHITECTURES" = '["arm64","x86_64"]' ] || {
721
+ echo "Release app manifest must bind exactly arm64 and x86_64." >&2
722
+ exit 1
723
+ }
724
+
725
+ PKG_SIGNATURE_OUTPUT="$(/usr/sbin/pkgutil --check-signature "$PKG")"
726
+ INSTALLER_CERTIFICATE_SHA256="$(printf '%s\n' "$PKG_SIGNATURE_OUTPUT" | \
727
+ /usr/bin/awk -f "$PKGUTIL_FINGERPRINT_PARSER")" || {
728
+ echo "Could not parse the Developer ID Installer certificate fingerprint." >&2
729
+ exit 1
730
+ }
731
+ [[ "$INSTALLER_CERTIFICATE_SHA256" =~ ^[a-f0-9]{64}$ ]] || {
732
+ echo "Could not bind the Developer ID Installer certificate fingerprint." >&2
733
+ exit 1
734
+ }
735
+
736
+ "$BUN_EXECUTABLE" "$SOURCE_ROOT/packaging/macos/release_lifecycle.ts" write-compatible-cohort \
737
+ --artifact-verifier-designated-requirement "$VERIFIER_REQUIREMENT" \
738
+ --artifact-verifier-sha256 "$VERIFIER_DIGEST" \
739
+ --bootstrap-marker-sha256 "$BOOTSTRAP_MARKER_SHA256" \
740
+ --envelope-public-key-sha256 "$PUBLIC_KEY_DIGEST" \
741
+ --installer-certificate-sha256 "$INSTALLER_CERTIFICATE_SHA256" \
742
+ --key-epoch "$KEY_EPOCH" \
743
+ --minimum-broker-version 1.0.0 \
744
+ --output "$COMPATIBLE_COHORT" \
745
+ --package-sha256 "$PKG_DIGEST" \
746
+ --team-id "$EXPECTED_TEAM_ID" \
747
+ --update-broker-designated-requirement "$BROKER_REQUIREMENT" \
748
+ --update-broker-sha256 "$BROKER_DIGEST"
749
+ [ -f "$COMPATIBLE_COHORT" ] && [ ! -L "$COMPATIBLE_COHORT" ] && [ -s "$COMPATIBLE_COHORT" ] || {
750
+ echo "Compatible-cohort helper did not emit a schema-v2 manifest." >&2
751
+ exit 1
752
+ }
753
+ COMPATIBLE_COHORT_DIGEST="$(sha256_file "$COMPATIBLE_COHORT")"
754
+ /usr/bin/printf '%s %s\n' "$COMPATIBLE_COHORT_DIGEST" "$COMPATIBLE_COHORT_LEAF" \
755
+ >"$COMPATIBLE_COHORT_SHA256"
756
+
757
+ ISSUED_AT_UTC="$(/bin/date -u '+%Y-%m-%dT%H:%M:%S.000Z')"
758
+ /usr/bin/plutil -create json "$ENVELOPE_PAYLOAD"
759
+ /usr/bin/plutil -insert purpose -string bootstrap "$ENVELOPE_PAYLOAD"
760
+ /usr/bin/plutil -insert schema_version -integer 1 "$ENVELOPE_PAYLOAD"
761
+ /usr/bin/plutil -insert key_epoch -integer "$KEY_EPOCH" "$ENVELOPE_PAYLOAD"
762
+ /usr/bin/plutil -insert release_sequence -integer "$RELEASE_SEQUENCE" "$ENVELOPE_PAYLOAD"
763
+ /usr/bin/plutil -insert release_id -string "$RELEASE_ID" "$ENVELOPE_PAYLOAD"
764
+ /usr/bin/plutil -insert version -string "$VERSION" "$ENVELOPE_PAYLOAD"
765
+ /usr/bin/plutil -insert build -string "$(/usr/bin/plutil -extract bundle_build_version raw -o - "$MANIFEST")" "$ENVELOPE_PAYLOAD"
766
+ /usr/bin/plutil -insert source_commit -string "$SOURCE_SHA" "$ENVELOPE_PAYLOAD"
767
+ /usr/bin/plutil -insert artifact_sha256 -string "$PKG_DIGEST" "$ENVELOPE_PAYLOAD"
768
+ /usr/bin/plutil -insert artifact_byte_count -integer "$(/usr/bin/stat -f '%z' "$PKG")" "$ENVELOPE_PAYLOAD"
769
+ /usr/bin/plutil -insert manifest_sha256 -string "$MANIFEST_SHA256" "$ENVELOPE_PAYLOAD"
770
+ /usr/bin/plutil -insert manifest_byte_count -integer "$(/usr/bin/stat -f '%z' "$MANIFEST")" "$ENVELOPE_PAYLOAD"
771
+ /usr/bin/plutil -insert candidate_tree_sha256 -string "$STAGED_APP_TREE_SHA256" "$ENVELOPE_PAYLOAD"
772
+ /usr/bin/plutil -insert package_sha256 -string "$PKG_DIGEST" "$ENVELOPE_PAYLOAD"
773
+ /usr/bin/plutil -insert bootstrap_marker_sha256 -string "$BOOTSTRAP_MARKER_SHA256" "$ENVELOPE_PAYLOAD"
774
+ /usr/bin/plutil -insert update_client_sha256 -string "$CLIENT_DIGEST" "$ENVELOPE_PAYLOAD"
775
+ /usr/bin/plutil -insert update_broker_sha256 -string "$BROKER_DIGEST" "$ENVELOPE_PAYLOAD"
776
+ /usr/bin/plutil -insert artifact_verifier_sha256 -string "$VERIFIER_DIGEST" "$ENVELOPE_PAYLOAD"
777
+ /usr/bin/plutil -insert architectures -json '["arm64","x86_64"]' "$ENVELOPE_PAYLOAD"
778
+ /usr/bin/plutil -insert minimum_os_version -string "$(/usr/bin/plutil -extract minimum_macos raw -o - "$MANIFEST")" "$ENVELOPE_PAYLOAD"
779
+ /usr/bin/plutil -insert minimum_broker_version -string 1.0.0 "$ENVELOPE_PAYLOAD"
780
+ /usr/bin/plutil -insert signing_team_identifier -string "$EXPECTED_TEAM_ID" "$ENVELOPE_PAYLOAD"
781
+ /usr/bin/plutil -insert application_designated_requirement -string "$APP_REQUIREMENT" "$ENVELOPE_PAYLOAD"
782
+ /usr/bin/plutil -insert update_client_designated_requirement -string "$CLIENT_REQUIREMENT" "$ENVELOPE_PAYLOAD"
783
+ /usr/bin/plutil -insert update_broker_designated_requirement -string "$BROKER_REQUIREMENT" "$ENVELOPE_PAYLOAD"
784
+ /usr/bin/plutil -insert artifact_verifier_designated_requirement -string "$VERIFIER_REQUIREMENT" "$ENVELOPE_PAYLOAD"
785
+ /usr/bin/plutil -insert installer_certificate_sha256 -string "$INSTALLER_CERTIFICATE_SHA256" "$ENVELOPE_PAYLOAD"
786
+ /usr/bin/plutil -insert issued_at_utc -string "$ISSUED_AT_UTC" "$ENVELOPE_PAYLOAD"
787
+ /usr/bin/plutil -insert expires_at_utc -string "$EXPIRES_AT_UTC" "$ENVELOPE_PAYLOAD"
788
+
789
+ "$ENVELOPE_SIGNER" \
790
+ --payload "$ENVELOPE_PAYLOAD" \
791
+ --private-key "$ENVELOPE_PRIVATE_KEY" \
792
+ --public-key "$PUBLIC_KEY" \
793
+ --output "$BOOTSTRAP_ENVELOPE"
794
+ [ -s "$BOOTSTRAP_ENVELOPE" ] || { echo "Envelope signer did not emit a bootstrap sidecar." >&2; exit 1; }
795
+
796
+ verify_package_release_set() {
797
+ local output
798
+ for output in \
799
+ "$PKG" \
800
+ "$NOTARY_SUBMISSION" \
801
+ "$NOTARY_LOG" \
802
+ "$PKG_SHA256" \
803
+ "$BOOTSTRAP_ENVELOPE" \
804
+ "$COMPATIBLE_COHORT" \
805
+ "$COMPATIBLE_COHORT_SHA256"; do
806
+ [ -f "$output" ] && [ ! -L "$output" ] && [ -s "$output" ] || {
807
+ echo "Package release staging is incomplete or unsafe: $output" >&2
808
+ exit 1
809
+ }
810
+ require_no_extended_acl "$output"
811
+ /bin/chmod 0444 "$output"
812
+ done
813
+ [ "$(/usr/bin/awk 'NR == 1 { print $1 }' "$PKG_SHA256")" = "$(sha256_file "$PKG")" ] || {
814
+ echo "Published package digest does not match the finalized PKG bytes." >&2
815
+ exit 1
816
+ }
817
+ [ "$(/usr/bin/awk 'NR == 1 { print $2 }' "$PKG_SHA256")" = "$PKG_LEAF" ] || {
818
+ echo "Published package digest names the wrong PKG artifact." >&2
819
+ exit 1
820
+ }
821
+ [ "$(/usr/bin/awk 'NR == 1 { print $1 }' "$COMPATIBLE_COHORT_SHA256")" = "$(sha256_file "$COMPATIBLE_COHORT")" ] || {
822
+ echo "Published compatible-cohort digest does not match the schema-v2 manifest." >&2
823
+ exit 1
824
+ }
825
+ [ "$(/usr/bin/awk 'NR == 1 { print $2 }' "$COMPATIBLE_COHORT_SHA256")" = "$COMPATIBLE_COHORT_LEAF" ] || {
826
+ echo "Published compatible-cohort digest names the wrong manifest." >&2
827
+ exit 1
828
+ }
829
+ }
830
+
831
+ verify_package_release_set
832
+ require_no_extended_acl "$PACKAGE_STAGE_DIR"
833
+ [ ! -e "$PACKAGE_FINAL_DIR" ] && [ ! -L "$PACKAGE_FINAL_DIR" ] || {
834
+ echo "Package release destination appeared before publication." >&2
835
+ exit 1
836
+ }
837
+ "$STAGED_VERIFIER" prepare-release-publication \
838
+ --staging "$PACKAGE_STAGE_DIR" \
839
+ --destination "$PACKAGE_FINAL_DIR" \
840
+ --reservation "$PACKAGE_RESERVATION" \
841
+ --publication-identity-sha256 "$PUBLICATION_IDENTITY_SHA256" \
842
+ --alias "$PKG_LEAF" \
843
+ --alias "$NOTARY_SUBMISSION_LEAF" \
844
+ --alias "$NOTARY_LOG_LEAF" \
845
+ --alias "$PKG_SHA256_LEAF" \
846
+ --alias "$BOOTSTRAP_ENVELOPE_LEAF" \
847
+ --alias "$COMPATIBLE_COHORT_LEAF" \
848
+ --alias "$COMPATIBLE_COHORT_SHA256_LEAF"
849
+ "$STAGED_VERIFIER" publish-release-directory \
850
+ --staging "$PACKAGE_STAGE_DIR" \
851
+ --destination "$PACKAGE_FINAL_DIR"
852
+ PACKAGE_DIRECTORY_PUBLISHED=1
853
+ PACKAGE_STAGE_DIR=""
854
+ "$STAGED_VERIFIER" complete-release-publication \
855
+ --destination "$PACKAGE_FINAL_DIR" \
856
+ --reservation "$PACKAGE_RESERVATION" \
857
+ --output-root "$OUTPUT_DIR" \
858
+ --publication-identity-sha256 "$PUBLICATION_IDENTITY_SHA256"
859
+ "$STAGED_VERIFIER" assert-release-publication-complete \
860
+ --destination "$PACKAGE_FINAL_DIR" \
861
+ --output-root "$OUTPUT_DIR" \
862
+ --publication-identity-sha256 "$PUBLICATION_IDENTITY_SHA256"
863
+ PACKAGE_RESERVATION_OWNED=0
864
+
865
+ PKG="$OUTPUT_DIR/$PKG_LEAF"
866
+ BOOTSTRAP_ENVELOPE="$OUTPUT_DIR/$BOOTSTRAP_ENVELOPE_LEAF"
867
+ COMPATIBLE_COHORT="$OUTPUT_DIR/$COMPATIBLE_COHORT_LEAF"
868
+ COMPATIBLE_COHORT_SHA256="$OUTPUT_DIR/$COMPATIBLE_COHORT_SHA256_LEAF"
869
+ printf 'Built signed, notarized updater PKG from source %s: %s\n' "$SOURCE_SHA" "$PKG"
870
+ printf 'Built signed external bootstrap envelope: %s\n' "$BOOTSTRAP_ENVELOPE"
871
+ printf 'Built schema-v2 compatible-cohort manifest: %s\n' "$COMPATIBLE_COHORT"
872
+ printf 'Built compatible-cohort manifest digest: %s\n' "$COMPATIBLE_COHORT_SHA256"