@empire-builder-kit/nx 1.0.0-rc.4 → 1.0.0-rc.41

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 (805) hide show
  1. package/README.md +44 -10
  2. package/executors.json +21 -1
  3. package/generators.json +10 -0
  4. package/package.json +31 -7
  5. package/src/aws/child-environment.d.ts +10 -0
  6. package/src/aws/child-environment.js +77 -0
  7. package/src/aws/identity.d.ts +13 -0
  8. package/src/aws/identity.js +41 -0
  9. package/src/aws-authority/index.d.ts +2 -0
  10. package/src/aws-authority/index.js +6 -0
  11. package/src/aws-authority/lambda-policy-ordering.d.ts +38 -0
  12. package/src/aws-authority/lambda-policy-ordering.js +56 -0
  13. package/src/aws-authority/stage-authority.d.ts +248 -0
  14. package/src/aws-authority/stage-authority.js +3558 -0
  15. package/src/blueprints/external-sandbox.d.ts +1 -1
  16. package/src/blueprints/external-sandbox.js +21 -9
  17. package/src/blueprints/manifest.d.ts +1 -1
  18. package/src/blueprints/resolver.js +28 -15
  19. package/src/cli/bootstrap.d.ts +1 -1
  20. package/src/cli/deploy-runtime.d.ts +30 -0
  21. package/src/cli/deploy-runtime.js +174 -0
  22. package/src/cli/dev-command.d.ts +18 -4
  23. package/src/cli/dev-command.js +71 -0
  24. package/src/cli/main.d.ts +9 -1
  25. package/src/cli/main.js +157 -24
  26. package/src/cli/output.d.ts +1 -1
  27. package/src/cli/output.js +1 -1
  28. package/src/cli/personal-secret-runtime.d.ts +12 -0
  29. package/src/cli/personal-secret-runtime.js +18 -3
  30. package/src/cli/secret-terminal.js +1 -1
  31. package/src/container-images.d.ts +1 -1
  32. package/src/container-images.js +1 -1
  33. package/src/developer-agent/context.js +41 -9
  34. package/src/developer-agent/generator-planning.js +20 -6
  35. package/src/developer-agent/git-inspection.d.ts +6 -2
  36. package/src/developer-agent/git-inspection.js +78 -6
  37. package/src/developer-agent/model.d.ts +5 -3
  38. package/src/developer-agent/model.js +4 -4
  39. package/src/developer-agent/operation-registry.d.ts +15 -0
  40. package/src/developer-agent/operation-registry.js +342 -49
  41. package/src/developer-agent/planning.d.ts +8 -1
  42. package/src/developer-agent/planning.js +39 -16
  43. package/src/developer-agent/preview-changes.d.ts +2 -0
  44. package/src/developer-agent/preview-changes.js +14 -2
  45. package/src/developer-agent/preview-source.js +3 -0
  46. package/src/developer-agent/validation.js +25 -9
  47. package/src/developer-agent/workspace-inspection.js +60 -5
  48. package/src/executors/blueprint-trust/blueprint-trust.js +6 -0
  49. package/src/executors/deployment-plan/deployment-plan.d.ts +2 -0
  50. package/src/executors/deployment-plan/deployment-plan.js +57 -2
  51. package/src/executors/dev-doctor/dev-doctor.js +13 -6
  52. package/src/executors/dev-task-cleanup/dev-task-cleanup.d.ts +23 -0
  53. package/src/executors/dev-task-cleanup/dev-task-cleanup.js +145 -0
  54. package/src/executors/dev-task-cleanup/schema.json +9 -0
  55. package/src/executors/dev-task-cleanup/task-stack-absence.d.ts +27 -0
  56. package/src/executors/dev-task-cleanup/task-stack-absence.js +281 -0
  57. package/src/executors/dev-task-cleanup/task-stack-checkpoint.d.ts +12 -0
  58. package/src/executors/dev-task-cleanup/task-stack-checkpoint.js +92 -0
  59. package/src/executors/fleet/fleet.js +1 -1
  60. package/src/executors/foundation-preflight/foundation-preflight.d.ts +1 -0
  61. package/src/executors/foundation-preflight/foundation-preflight.js +6 -2
  62. package/src/executors/foundation-preflight/schema.json +5 -0
  63. package/src/executors/foundation-profile-preflight/foundation-profile-preflight.d.ts +13 -2
  64. package/src/executors/foundation-profile-preflight/foundation-profile-preflight.js +95 -31
  65. package/src/executors/foundation-profile-preflight/production-database-safeguard.d.ts +65 -0
  66. package/src/executors/foundation-profile-preflight/production-database-safeguard.js +223 -0
  67. package/src/executors/foundation-profile-preflight/schema.json +15 -4
  68. package/src/executors/foundation-remove-preflight/foundation-remove-preflight.d.ts +11 -1
  69. package/src/executors/foundation-remove-preflight/foundation-remove-preflight.js +70 -20
  70. package/src/executors/foundation-remove-preflight/schema.json +6 -1
  71. package/src/executors/github-bootstrap/github-bootstrap.d.ts +1 -0
  72. package/src/executors/github-bootstrap/github-bootstrap.js +3 -0
  73. package/src/executors/github-bootstrap/schema.json +7 -1
  74. package/src/executors/infrastructure-plan/infrastructure-plan.d.ts +29 -136
  75. package/src/executors/infrastructure-plan/infrastructure-plan.js +230 -3282
  76. package/src/executors/infrastructure-plan/schema.json +12 -52
  77. package/src/executors/local-deploy/federated-credential-transport.d.ts +48 -0
  78. package/src/executors/local-deploy/federated-credential-transport.js +252 -0
  79. package/src/executors/local-deploy/local-approval.d.ts +18 -0
  80. package/src/executors/local-deploy/local-approval.js +39 -0
  81. package/src/executors/local-deploy/local-deploy.d.ts +112 -0
  82. package/src/executors/local-deploy/local-deploy.js +1404 -0
  83. package/src/executors/local-deploy/schema.json +70 -0
  84. package/src/executors/refresh/refresh.js +2 -2
  85. package/src/executors/remove/remove.js +2 -2
  86. package/src/executors/router-waf-rehearsal/router-waf-rehearsal.d.ts +42 -0
  87. package/src/executors/router-waf-rehearsal/router-waf-rehearsal.js +1229 -0
  88. package/src/executors/router-waf-rehearsal/schema.json +29 -0
  89. package/src/executors/secret-initialize/schema.json +42 -0
  90. package/src/executors/secret-initialize/secret-initialize.d.ts +34 -0
  91. package/src/executors/secret-initialize/secret-initialize.js +297 -0
  92. package/src/executors/secret-metadata/schema.json +5 -0
  93. package/src/executors/secret-metadata/secret-metadata.d.ts +16 -2
  94. package/src/executors/secret-metadata/secret-metadata.js +129 -30
  95. package/src/executors/sst-lifecycle/generated-secret-retirement.d.ts +53 -0
  96. package/src/executors/sst-lifecycle/generated-secret-retirement.js +192 -0
  97. package/src/executors/sst-lifecycle/governed-native-operation.d.ts +48 -0
  98. package/src/executors/sst-lifecycle/governed-native-operation.js +163 -0
  99. package/src/executors/sst-lifecycle/logging-preflight.d.ts +5 -0
  100. package/src/executors/sst-lifecycle/logging-preflight.js +23 -13
  101. package/src/executors/sst-lifecycle/run-sst-command.d.ts +46 -6
  102. package/src/executors/sst-lifecycle/run-sst-command.js +750 -71
  103. package/src/executors/sst-lifecycle/schema.json +8 -1
  104. package/src/executors/sst-lifecycle/secret-preflight.d.ts +2 -0
  105. package/src/executors/sst-lifecycle/secret-preflight.js +13 -7
  106. package/src/executors/sst-lifecycle/sst-environment-source.d.ts +33 -0
  107. package/src/executors/sst-lifecycle/sst-environment-source.js +267 -0
  108. package/src/executors/sst-lifecycle/task-unlock.d.ts +15 -0
  109. package/src/executors/sst-lifecycle/task-unlock.js +126 -0
  110. package/src/executors/unlock/unlock.js +2 -2
  111. package/src/foundation/capability-plan.d.ts +43 -0
  112. package/src/foundation/capability-plan.js +168 -0
  113. package/src/foundation/cost-estimate.d.ts +170 -0
  114. package/src/foundation/cost-estimate.js +683 -0
  115. package/src/foundation/discovery.d.ts +14 -11
  116. package/src/foundation/discovery.js +32 -6
  117. package/src/foundation/index.d.ts +4 -0
  118. package/src/foundation/index.js +11 -0
  119. package/src/foundation/local-database-contract.d.ts +20 -0
  120. package/src/foundation/local-database-contract.js +262 -13
  121. package/src/foundation/local-private-smoke-access.d.ts +20 -0
  122. package/src/foundation/local-private-smoke-access.js +197 -0
  123. package/src/foundation/logging-preflight.js +1 -1
  124. package/src/foundation/logging.d.ts +3 -1
  125. package/src/foundation/logging.js +1 -1
  126. package/src/foundation/partial-state.d.ts +10 -0
  127. package/src/foundation/partial-state.js +21 -0
  128. package/src/foundation/preflight.js +37 -18
  129. package/src/foundation/private-smoke-access.d.ts +25 -0
  130. package/src/foundation/private-smoke-access.js +135 -0
  131. package/src/foundation/private-smoke-admission.d.ts +45 -0
  132. package/src/foundation/private-smoke-admission.js +180 -0
  133. package/src/foundation/private-smoke-control-plane.d.ts +18 -0
  134. package/src/foundation/private-smoke-control-plane.js +184 -0
  135. package/src/foundation/provision-database-bindings.d.ts +5 -0
  136. package/src/foundation/provision-database-bindings.js +41 -8
  137. package/src/foundation/provision-logical-database.js +15 -8
  138. package/src/foundation/requirements.d.ts +3 -3
  139. package/src/foundation/requirements.js +24 -12
  140. package/src/foundation/router-waf-rate-rehearsal.d.ts +97 -0
  141. package/src/foundation/router-waf-rate-rehearsal.js +210 -0
  142. package/src/foundation/router-waf.d.ts +149 -0
  143. package/src/foundation/router-waf.js +809 -0
  144. package/src/foundation/stages.d.ts +1 -0
  145. package/src/foundation/stages.js +11 -0
  146. package/src/generators/blueprint/blueprint.d.ts +5 -0
  147. package/src/generators/blueprint/blueprint.js +102 -0
  148. package/src/generators/blueprint/files.d.ts +6 -0
  149. package/src/generators/blueprint/files.js +217 -0
  150. package/src/generators/blueprint/schema.d.ts +4 -0
  151. package/src/generators/blueprint/schema.json +18 -0
  152. package/src/generators/formatting.d.ts +8 -0
  153. package/src/generators/formatting.js +141 -0
  154. package/src/generators/function/function.js +38 -17
  155. package/src/generators/logging-registry.d.ts +2 -0
  156. package/src/generators/logging-registry.js +22 -0
  157. package/src/generators/post-generation-install.js +2 -2
  158. package/src/generators/preset/dependency-overrides.d.ts +8 -0
  159. package/src/generators/preset/dependency-overrides.js +55 -0
  160. package/src/generators/preset/generator.js +43 -3
  161. package/src/generators/preset/schema.d.ts +10 -0
  162. package/src/generators/preset/schema.json +47 -0
  163. package/src/generators/preset/workspace-files-adopter-skills.d.ts +7 -0
  164. package/src/generators/preset/workspace-files-adopter-skills.js +324 -0
  165. package/src/generators/preset/workspace-files-ci-acceptance.d.ts +1 -0
  166. package/src/generators/preset/workspace-files-ci-acceptance.js +791 -0
  167. package/src/generators/preset/workspace-files-ci-install.d.ts +2 -0
  168. package/src/generators/preset/workspace-files-ci-install.js +196 -0
  169. package/src/generators/preset/workspace-files-developer-agent.d.ts +2 -0
  170. package/src/generators/preset/workspace-files-developer-agent.js +162 -44
  171. package/src/generators/preset/workspace-files-ecr-inspector.d.ts +18 -0
  172. package/src/generators/preset/workspace-files-ecr-inspector.js +298 -0
  173. package/src/generators/preset/workspace-files-foundation-workload-roles.js +5 -2
  174. package/src/generators/preset/workspace-files-performance-cost.d.ts +2 -2
  175. package/src/generators/preset/workspace-files-performance-cost.js +8 -8
  176. package/src/generators/preset/workspace-files-performance-load.d.ts +1 -1
  177. package/src/generators/preset/workspace-files-performance-load.js +39 -25
  178. package/src/generators/preset/workspace-files-shared-operation-claims.d.ts +10 -0
  179. package/src/generators/preset/workspace-files-shared-operation-claims.js +59 -0
  180. package/src/generators/preset/workspace-files-stage-authority.d.ts +6 -0
  181. package/src/generators/preset/workspace-files-stage-authority.js +529 -0
  182. package/src/generators/preset/workspace-files-thin-deploy.d.ts +65 -0
  183. package/src/generators/preset/workspace-files-thin-deploy.js +2507 -0
  184. package/src/generators/preset/workspace-files-thin-docs.d.ts +7 -0
  185. package/src/generators/preset/workspace-files-thin-docs.js +370 -0
  186. package/src/generators/preset/workspace-files-thin-release.d.ts +14 -0
  187. package/src/generators/preset/workspace-files-thin-release.js +26 -0
  188. package/src/generators/preset/workspace-files-workload-role-baseline.js +254 -42
  189. package/src/generators/preset/workspace-files.d.ts +18 -2
  190. package/src/generators/preset/workspace-files.js +1525 -609
  191. package/src/generators/repository-policy/repository-policy.js +235 -5
  192. package/src/generators/repository-policy/schema.d.ts +10 -0
  193. package/src/generators/repository-policy/schema.json +80 -4
  194. package/src/generators/repository-policy/tree-policy.d.ts +3 -0
  195. package/src/generators/repository-policy/tree-policy.js +25 -0
  196. package/src/generators/slice/built-in-plan-api-contract-files.js +49 -11
  197. package/src/generators/slice/built-in-plan-api-files.d.ts +1 -1
  198. package/src/generators/slice/built-in-plan-api-files.js +130 -414
  199. package/src/generators/slice/built-in-plan-async-work-files.js +166 -1969
  200. package/src/generators/slice/built-in-plan-authorization-files.js +102 -334
  201. package/src/generators/slice/built-in-plan-authorization-infrastructure-files.js +975 -233
  202. package/src/generators/slice/built-in-plan-authorization-storage.d.ts +3 -0
  203. package/src/generators/slice/built-in-plan-authorization-storage.js +119 -0
  204. package/src/generators/slice/built-in-plan-background-work-files.d.ts +2 -0
  205. package/src/generators/slice/built-in-plan-background-work-files.js +99 -0
  206. package/src/generators/slice/built-in-plan-data-deletion-files.js +2 -830
  207. package/src/generators/slice/built-in-plan-data-export-infrastructure-files.js +5 -31
  208. package/src/generators/slice/built-in-plan-data-files.d.ts +1 -1
  209. package/src/generators/slice/built-in-plan-data-files.js +176 -1200
  210. package/src/generators/slice/built-in-plan-data-lifecycle-files.js +53 -28
  211. package/src/generators/slice/built-in-plan-data-lifecycle-runtime-files.js +37 -2
  212. package/src/generators/slice/built-in-plan-data-retention-job-files.js +7 -2443
  213. package/src/generators/slice/built-in-plan-data-retention-launcher-files.js +50 -8
  214. package/src/generators/slice/built-in-plan-data-rights-files.js +6 -4482
  215. package/src/generators/slice/built-in-plan-database-lifecycle-files.js +44 -25
  216. package/src/generators/slice/built-in-plan-database-retirement-rehearsal-files.js +25 -2
  217. package/src/generators/slice/built-in-plan-foundation-files.d.ts +2 -1
  218. package/src/generators/slice/built-in-plan-foundation-files.js +39 -18
  219. package/src/generators/slice/built-in-plan-frontend-files.js +14 -3
  220. package/src/generators/slice/built-in-plan-infrastructure-files.d.ts +1 -1
  221. package/src/generators/slice/built-in-plan-infrastructure-files.js +933 -730
  222. package/src/generators/slice/built-in-plan-infrastructure-safety-files.js +75 -14
  223. package/src/generators/slice/built-in-plan-key-value-cleanup.d.ts +4 -0
  224. package/src/generators/slice/built-in-plan-key-value-cleanup.js +100 -0
  225. package/src/generators/slice/built-in-plan-key-value-consumer.d.ts +4 -0
  226. package/src/generators/slice/built-in-plan-key-value-consumer.js +151 -0
  227. package/src/generators/slice/built-in-plan-key-value-deletion.d.ts +2 -0
  228. package/src/generators/slice/built-in-plan-key-value-deletion.js +377 -0
  229. package/src/generators/slice/built-in-plan-key-value-delivery.d.ts +2 -0
  230. package/src/generators/slice/built-in-plan-key-value-delivery.js +137 -0
  231. package/src/generators/slice/built-in-plan-key-value-export.d.ts +2 -0
  232. package/src/generators/slice/built-in-plan-key-value-export.js +595 -0
  233. package/src/generators/slice/built-in-plan-key-value-files.d.ts +2 -0
  234. package/src/generators/slice/built-in-plan-key-value-files.js +31 -0
  235. package/src/generators/slice/built-in-plan-key-value-holds.d.ts +2 -0
  236. package/src/generators/slice/built-in-plan-key-value-holds.js +189 -0
  237. package/src/generators/slice/built-in-plan-key-value-infrastructure.d.ts +2 -0
  238. package/src/generators/slice/built-in-plan-key-value-infrastructure.js +102 -0
  239. package/src/generators/slice/built-in-plan-key-value-performance.d.ts +5 -0
  240. package/src/generators/slice/built-in-plan-key-value-performance.js +530 -0
  241. package/src/generators/slice/built-in-plan-key-value-provider.d.ts +4 -0
  242. package/src/generators/slice/built-in-plan-key-value-provider.js +366 -0
  243. package/src/generators/slice/built-in-plan-key-value-records.d.ts +3 -0
  244. package/src/generators/slice/built-in-plan-key-value-records.js +232 -0
  245. package/src/generators/slice/built-in-plan-key-value-retention-infrastructure.d.ts +2 -0
  246. package/src/generators/slice/built-in-plan-key-value-retention-infrastructure.js +103 -0
  247. package/src/generators/slice/built-in-plan-key-value-retention-job-files.d.ts +2 -0
  248. package/src/generators/slice/built-in-plan-key-value-retention-job-files.js +160 -0
  249. package/src/generators/slice/built-in-plan-key-value-retention-maintenance.d.ts +2 -0
  250. package/src/generators/slice/built-in-plan-key-value-retention-maintenance.js +329 -0
  251. package/src/generators/slice/built-in-plan-key-value-retention-worker.d.ts +2 -0
  252. package/src/generators/slice/built-in-plan-key-value-retention-worker.js +477 -0
  253. package/src/generators/slice/built-in-plan-key-value-retention.d.ts +2 -0
  254. package/src/generators/slice/built-in-plan-key-value-retention.js +184 -0
  255. package/src/generators/slice/built-in-plan-metadata-files.d.ts +1 -1
  256. package/src/generators/slice/built-in-plan-metadata-files.js +159 -29
  257. package/src/generators/slice/built-in-plan-native-runtime-authority-files.d.ts +2 -0
  258. package/src/generators/slice/built-in-plan-native-runtime-authority-files.js +324 -0
  259. package/src/generators/slice/built-in-plan-operations-files.d.ts +1 -1
  260. package/src/generators/slice/built-in-plan-operations-files.js +585 -97
  261. package/src/generators/slice/built-in-plan-performance-files.d.ts +2 -1
  262. package/src/generators/slice/built-in-plan-performance-files.js +220 -1074
  263. package/src/generators/slice/built-in-plan-project-files.js +141 -135
  264. package/src/generators/slice/built-in-plan-release-files.d.ts +1 -1
  265. package/src/generators/slice/built-in-plan-release-files.js +310 -70
  266. package/src/generators/slice/built-in-plan-reliability-files.d.ts +1 -1
  267. package/src/generators/slice/built-in-plan-reliability-files.js +283 -44
  268. package/src/generators/slice/built-in-plan-secret-runtime-files.d.ts +1 -1
  269. package/src/generators/slice/built-in-plan-secret-runtime-files.js +87 -76
  270. package/src/generators/slice/built-in-plan-shared.d.ts +4 -2
  271. package/src/generators/slice/built-in-plan-shared.js +41 -19
  272. package/src/generators/slice/built-in-plan-sql-events.d.ts +2 -0
  273. package/src/generators/slice/built-in-plan-sql-events.js +276 -0
  274. package/src/generators/slice/built-in-plan-testing-files.d.ts +1 -1
  275. package/src/generators/slice/built-in-plan-testing-files.js +304 -172
  276. package/src/generators/slice/built-in-plan-testing-persistence-event-files.d.ts +7 -0
  277. package/src/generators/slice/built-in-plan-testing-persistence-event-files.js +487 -0
  278. package/src/generators/slice/built-in-plan-testing-persistence-files.d.ts +2 -0
  279. package/src/generators/slice/built-in-plan-testing-persistence-files.js +180 -0
  280. package/src/generators/slice/built-in-plan-thin-release-files.d.ts +11 -0
  281. package/src/generators/slice/built-in-plan-thin-release-files.js +162 -0
  282. package/src/generators/slice/built-in-plan-verification-files.js +76 -2
  283. package/src/generators/slice/built-in-plan-web-files.d.ts +1 -1
  284. package/src/generators/slice/built-in-plan-web-files.js +223 -16
  285. package/src/generators/slice/built-in-plan-workload-role-files.d.ts +1 -1
  286. package/src/generators/slice/built-in-plan-workload-role-files.js +143 -279
  287. package/src/generators/slice/built-in-plan.d.ts +11 -1
  288. package/src/generators/slice/built-in-plan.js +83 -30
  289. package/src/generators/slice/data-policy-review-seed.d.ts +2 -0
  290. package/src/generators/slice/data-policy-review-seed.js +22 -0
  291. package/src/generators/slice/deployed-smoke-readiness.d.ts +2 -0
  292. package/src/generators/slice/deployed-smoke-readiness.js +214 -0
  293. package/src/generators/slice/local-source-identity.d.ts +2 -0
  294. package/src/generators/slice/local-source-identity.js +31 -0
  295. package/src/generators/slice/persistence-contract.d.ts +14 -0
  296. package/src/generators/slice/persistence-contract.js +32 -0
  297. package/src/generators/slice/slice.js +61 -50
  298. package/src/generators/sql/schema.d.ts +7 -0
  299. package/src/generators/sql/schema.json +38 -0
  300. package/src/generators/sql/sql.d.ts +6 -0
  301. package/src/generators/sql/sql.js +53 -0
  302. package/src/generators/upgrade/sql-seeded-reconciliation.d.ts +2 -0
  303. package/src/generators/upgrade/sql-seeded-reconciliation.js +170 -0
  304. package/src/generators/upgrade/upgrade.d.ts +15 -1
  305. package/src/generators/upgrade/upgrade.js +435 -44
  306. package/src/generators/workload/frozen-container.d.ts +1 -1
  307. package/src/generators/workload/performance-registration.d.ts +1 -0
  308. package/src/generators/workload/performance-registration.js +9 -11
  309. package/src/generators/workload/service-performance-files.d.ts +1 -0
  310. package/src/generators/workload/service-performance-files.js +27 -7
  311. package/src/generators/workload/workload.d.ts +1 -1
  312. package/src/generators/workload/workload.js +105 -29
  313. package/src/git-policy/action-pins.d.ts +5 -0
  314. package/src/git-policy/action-pins.js +5 -0
  315. package/src/git-policy/github-bootstrap.d.ts +48 -8
  316. package/src/git-policy/github-bootstrap.js +344 -176
  317. package/src/git-policy/routing.d.ts +7 -1
  318. package/src/git-policy/routing.js +62 -1
  319. package/src/index.d.ts +1 -0
  320. package/src/index.js +1 -0
  321. package/src/infrastructure-change/local-capability.d.ts +73 -0
  322. package/src/infrastructure-change/local-capability.js +114 -0
  323. package/src/infrastructure-change/model.d.ts +25 -4
  324. package/src/infrastructure-change/model.js +1 -0
  325. package/src/infrastructure-change/normalize.d.ts +4 -0
  326. package/src/infrastructure-change/normalize.js +47 -7
  327. package/src/infrastructure-change/plan.d.ts +30 -3
  328. package/src/infrastructure-change/plan.js +323 -13
  329. package/src/infrastructure-change/risk.d.ts +3 -1
  330. package/src/infrastructure-change/risk.js +96 -16
  331. package/src/local-dev/browser-verification.d.ts +37 -0
  332. package/src/local-dev/browser-verification.js +281 -0
  333. package/src/local-dev/contracts.d.ts +10 -1
  334. package/src/local-dev/contracts.js +114 -13
  335. package/src/local-dev/dev-command-core.js +24 -5
  336. package/src/local-dev/docker-credential-health.d.ts +14 -0
  337. package/src/local-dev/docker-credential-health.js +94 -0
  338. package/src/local-dev/environment-inspector.d.ts +7 -0
  339. package/src/local-dev/environment-inspector.js +42 -15
  340. package/src/local-dev/environment-state.js +2 -2
  341. package/src/local-dev/errors.d.ts +1 -1
  342. package/src/local-dev/errors.js +2 -0
  343. package/src/local-dev/fleet-client.js +2 -2
  344. package/src/local-dev/fleet-configuration.js +57 -25
  345. package/src/local-dev/fleet-inventory.js +58 -22
  346. package/src/local-dev/fleet-ipc.d.ts +7 -0
  347. package/src/local-dev/fleet-ipc.js +14 -2
  348. package/src/local-dev/fleet-launcher.d.ts +6 -0
  349. package/src/local-dev/fleet-launcher.js +16 -4
  350. package/src/local-dev/fleet-logs.js +50 -22
  351. package/src/local-dev/fleet-process.d.ts +20 -1
  352. package/src/local-dev/fleet-process.js +197 -41
  353. package/src/local-dev/fleet-runner-evidence.d.ts +5 -0
  354. package/src/local-dev/fleet-runner-evidence.js +56 -0
  355. package/src/local-dev/fleet-runtime.js +268 -229
  356. package/src/local-dev/fleet-session.d.ts +2 -0
  357. package/src/local-dev/fleet-session.js +28 -11
  358. package/src/local-dev/fleet-slice-runner-daemon.js +4 -0
  359. package/src/local-dev/fleet-state.d.ts +5 -2
  360. package/src/local-dev/fleet-state.js +25 -4
  361. package/src/local-dev/fleet-supervisor.d.ts +3 -0
  362. package/src/local-dev/fleet-supervisor.js +66 -11
  363. package/src/local-dev/fleet-web-start.d.ts +12 -0
  364. package/src/local-dev/fleet-web-start.js +48 -0
  365. package/src/local-dev/identity.js +3 -14
  366. package/src/local-dev/index.d.ts +1 -0
  367. package/src/local-dev/index.js +1 -0
  368. package/src/local-dev/native-completion.d.ts +9 -0
  369. package/src/local-dev/native-completion.js +130 -0
  370. package/src/local-dev/nx-workspace.d.ts +4 -2
  371. package/src/local-dev/nx-workspace.js +11 -6
  372. package/src/local-dev/personal-stage.d.ts +4 -5
  373. package/src/local-dev/personal-stage.js +6 -54
  374. package/src/local-dev/port-claims.d.ts +13 -0
  375. package/src/local-dev/port-claims.js +46 -0
  376. package/src/local-dev/ports.d.ts +14 -2
  377. package/src/local-dev/ports.js +78 -3
  378. package/src/local-dev/private-state.d.ts +11 -0
  379. package/src/local-dev/private-state.js +146 -40
  380. package/src/local-dev/process-group.d.ts +4 -0
  381. package/src/local-dev/process-group.js +29 -0
  382. package/src/local-dev/process-identity.d.ts +8 -0
  383. package/src/local-dev/process-identity.js +31 -1
  384. package/src/local-dev/proxy.js +11 -12
  385. package/src/local-dev/runtime-paths.js +1 -1
  386. package/src/local-dev/slice-claim.js +1 -1
  387. package/src/local-dev/slice-runner.d.ts +15 -6
  388. package/src/local-dev/slice-runner.js +189 -54
  389. package/src/local-dev/task-attachment.d.ts +17 -0
  390. package/src/local-dev/task-attachment.js +73 -0
  391. package/src/local-dev/task-authority.d.ts +10 -0
  392. package/src/local-dev/task-authority.js +27 -0
  393. package/src/local-dev/task-identity.d.ts +33 -0
  394. package/src/local-dev/task-identity.js +418 -0
  395. package/src/local-dev/task-native-writer.d.ts +18 -0
  396. package/src/local-dev/task-native-writer.js +329 -0
  397. package/src/local-dev/tls.js +8 -2
  398. package/src/local-dev/web-launch.d.ts +9 -0
  399. package/src/local-dev/web-launch.js +130 -0
  400. package/src/ownership/composition.d.ts +2 -1
  401. package/src/ownership/composition.js +1 -0
  402. package/src/performance/cost-collection.d.ts +1 -1
  403. package/src/performance/cost-collection.js +4 -2
  404. package/src/performance/cost-controls.d.ts +8 -4
  405. package/src/performance/cost-controls.js +39 -28
  406. package/src/performance/evidence.d.ts +1 -1
  407. package/src/performance/evidence.js +5 -3
  408. package/src/performance/load-safety.js +5 -3
  409. package/src/performance/model.d.ts +4 -4
  410. package/src/performance/model.js +4 -5
  411. package/src/performance/scenario.d.ts +22 -0
  412. package/src/performance/scenario.js +100 -92
  413. package/src/release/index.d.ts +0 -2
  414. package/src/release/index.js +0 -2
  415. package/src/release/source-authority.d.ts +5 -5
  416. package/src/release/source-authority.js +141 -124
  417. package/src/repository-policy/aurora-preflight.d.ts +1 -1
  418. package/src/repository-policy/aurora-preflight.js +4 -1
  419. package/src/repository-policy/capacity-migration.d.ts +3 -0
  420. package/src/repository-policy/capacity-migration.js +21 -0
  421. package/src/repository-policy/deployment-plan.d.ts +4 -1
  422. package/src/repository-policy/deployment-plan.js +9 -0
  423. package/src/repository-policy/foundation-profile.js +10 -1
  424. package/src/repository-policy/policy.d.ts +57 -3
  425. package/src/repository-policy/policy.js +497 -11
  426. package/src/schemas/blueprint-v1.json +5 -4
  427. package/src/schemas/foundation-requirement-v1.json +5 -4
  428. package/src/schemas/performance-policy-v1.json +3 -3
  429. package/src/schemas/repository-policy-v1.json +130 -6
  430. package/src/schemas/testing-evidence-result-v1.json +1 -1
  431. package/src/secrets/adapters.d.ts +7 -2
  432. package/src/secrets/adapters.js +34 -3
  433. package/src/secrets/aws-control-plane.d.ts +10 -1
  434. package/src/secrets/aws-control-plane.js +67 -0
  435. package/src/secrets/generated-aws-control-plane.d.ts +23 -0
  436. package/src/secrets/generated-aws-control-plane.js +259 -0
  437. package/src/secrets/metadata.js +38 -10
  438. package/src/secrets/model.d.ts +8 -1
  439. package/src/secrets/model.js +24 -3
  440. package/src/secrets/operator.d.ts +91 -3
  441. package/src/secrets/operator.js +463 -13
  442. package/src/secrets/personal-command.d.ts +18 -2
  443. package/src/secrets/personal-command.js +66 -12
  444. package/src/secrets/registry.d.ts +2 -1
  445. package/src/secrets/registry.js +18 -1
  446. package/src/secrets/verification.js +15 -2
  447. package/src/secrets/workload-access.js +10 -2
  448. package/src/sst-environment-source-format.d.ts +6 -0
  449. package/src/sst-environment-source-format.js +199 -0
  450. package/src/supply-chain/action-pins.js +61 -7
  451. package/src/supply-chain/blueprint-trust-git.js +85 -24
  452. package/src/supply-chain/blueprint-verification-authority.js +55 -3
  453. package/src/supply-chain/build-output-bounds.d.ts +2 -0
  454. package/src/supply-chain/build-output-bounds.js +12 -0
  455. package/src/supply-chain/build-output.d.ts +7 -1
  456. package/src/supply-chain/build-output.js +177 -24
  457. package/src/supply-chain/ecr-scanning.d.ts +5 -3
  458. package/src/supply-chain/ecr-scanning.js +11 -7
  459. package/src/supply-chain/ecs-deployed-image.d.ts +5 -3
  460. package/src/supply-chain/ecs-deployed-image.js +6 -4
  461. package/src/supply-chain/generated-blueprint-trust-command.js +18 -1
  462. package/src/supply-chain/generated-command.js +1 -0
  463. package/src/supply-chain/generated-container-release-command.js +1285 -124
  464. package/src/supply-chain/generated-ecr-collector-command.d.ts +0 -1
  465. package/src/supply-chain/generated-ecr-collector-command.js +200 -93
  466. package/src/supply-chain/generated-files.js +25 -14
  467. package/src/supply-chain/generated-scanner-command.js +2 -1
  468. package/src/supply-chain/index.d.ts +1 -1
  469. package/src/supply-chain/index.js +2 -1
  470. package/src/supply-chain/sbom.d.ts +1 -1
  471. package/src/supply-chain/sbom.js +30 -5
  472. package/src/supply-chain/scanner.js +3 -0
  473. package/src/testing-evidence/fixture-scope.d.ts +2 -1
  474. package/src/testing-evidence/fixture-scope.js +4 -2
  475. package/src/testing-evidence/result.d.ts +2 -1
  476. package/src/testing-evidence/result.js +18 -4
  477. package/src/workload-roles/account-baseline.d.ts +11 -0
  478. package/src/workload-roles/account-baseline.js +253 -16
  479. package/src/workload-roles/authority.d.ts +3 -1
  480. package/src/workload-roles/authority.js +48 -2
  481. package/src/workload-roles/aws-s3-state.js +27 -0
  482. package/src/workload-roles/boundary.d.ts +2 -1
  483. package/src/workload-roles/boundary.js +9 -2
  484. package/src/workload-roles/handler.js +52 -24
  485. package/src/workload-roles/index.d.ts +10 -17
  486. package/src/workload-roles/index.js +5 -18
  487. package/src/workload-roles/injection.js +1 -1
  488. package/src/workload-roles/provisioner.js +12 -4
  489. package/src/workload-roles/registration.d.ts +1 -0
  490. package/src/workload-roles/registration.js +5 -0
  491. package/src/workload-roles/stage-boundary.js +1 -1
  492. package/src/workload-roles/standard-slice.js +3 -3
  493. package/src/workspace-deployment/affected-lifecycle.d.ts +53 -0
  494. package/src/workspace-deployment/affected-lifecycle.js +84 -0
  495. package/src/workspace-deployment/identity.d.ts +3 -0
  496. package/src/workspace-deployment/identity.js +22 -0
  497. package/src/workspace-deployment/index.d.ts +4 -0
  498. package/src/workspace-deployment/index.js +8 -0
  499. package/src/workspace-deployment/migration-evidence.d.ts +29 -0
  500. package/src/workspace-deployment/migration-evidence.js +83 -0
  501. package/src/workspace-deployment/migration.d.ts +27 -0
  502. package/src/workspace-deployment/migration.js +109 -0
  503. package/src/workspace-deployment/ownership.d.ts +81 -0
  504. package/src/workspace-deployment/ownership.js +381 -0
  505. package/src/api-contract/baseline-update.js.map +0 -1
  506. package/src/api-contract/compatibility.js.map +0 -1
  507. package/src/api-contract/contract-graph.js.map +0 -1
  508. package/src/api-contract/evidence.js.map +0 -1
  509. package/src/api-contract/index.js.map +0 -1
  510. package/src/api-contract/model.fixtures.d.ts +0 -2
  511. package/src/api-contract/model.fixtures.js +0 -81
  512. package/src/api-contract/model.fixtures.js.map +0 -1
  513. package/src/api-contract/model.js.map +0 -1
  514. package/src/api-contract/rollout.js.map +0 -1
  515. package/src/authorization/compatibility.js.map +0 -1
  516. package/src/authorization/index.js.map +0 -1
  517. package/src/blueprints/execution.js.map +0 -1
  518. package/src/blueprints/external-sandbox.js.map +0 -1
  519. package/src/blueprints/index.js.map +0 -1
  520. package/src/blueprints/inputs.js.map +0 -1
  521. package/src/blueprints/manifest.js.map +0 -1
  522. package/src/blueprints/resolver.js.map +0 -1
  523. package/src/blueprints/transitions.js.map +0 -1
  524. package/src/cli/agent-command.js.map +0 -1
  525. package/src/cli/agent-runtime.js.map +0 -1
  526. package/src/cli/blueprint-authority.js.map +0 -1
  527. package/src/cli/blueprint-command.js.map +0 -1
  528. package/src/cli/bootstrap.js.map +0 -1
  529. package/src/cli/dev-command.js.map +0 -1
  530. package/src/cli/main.js.map +0 -1
  531. package/src/cli/output.js.map +0 -1
  532. package/src/cli/personal-secret-runtime.js.map +0 -1
  533. package/src/cli/secret-terminal.js.map +0 -1
  534. package/src/container-images.js.map +0 -1
  535. package/src/data-lifecycle/index.js.map +0 -1
  536. package/src/data-lifecycle/model.fixtures.d.ts +0 -2
  537. package/src/data-lifecycle/model.fixtures.js +0 -244
  538. package/src/data-lifecycle/model.fixtures.js.map +0 -1
  539. package/src/data-lifecycle/model.js.map +0 -1
  540. package/src/data-lifecycle/sensitive-artifacts.js.map +0 -1
  541. package/src/data-lifecycle/validation.js.map +0 -1
  542. package/src/database-retirement/contract.js.map +0 -1
  543. package/src/database-retirement/index.js.map +0 -1
  544. package/src/deterministic-order.js.map +0 -1
  545. package/src/developer-agent/context.js.map +0 -1
  546. package/src/developer-agent/generator-planning.js.map +0 -1
  547. package/src/developer-agent/generator-preview.js.map +0 -1
  548. package/src/developer-agent/git-inspection.js.map +0 -1
  549. package/src/developer-agent/handoff.js.map +0 -1
  550. package/src/developer-agent/index.js.map +0 -1
  551. package/src/developer-agent/model.js.map +0 -1
  552. package/src/developer-agent/operation-registry.js.map +0 -1
  553. package/src/developer-agent/ordering.js.map +0 -1
  554. package/src/developer-agent/planning.js.map +0 -1
  555. package/src/developer-agent/preview-changes.js.map +0 -1
  556. package/src/developer-agent/preview-source.js.map +0 -1
  557. package/src/developer-agent/validation.js.map +0 -1
  558. package/src/developer-agent/workspace-inspection.js.map +0 -1
  559. package/src/developer-agent/workspace-path.js.map +0 -1
  560. package/src/executors/blueprint-trust/blueprint-trust.js.map +0 -1
  561. package/src/executors/blueprint-verification/blueprint-verification.js.map +0 -1
  562. package/src/executors/deploy/deploy.js.map +0 -1
  563. package/src/executors/deployment-plan/deployment-plan.js.map +0 -1
  564. package/src/executors/dev/dev.js.map +0 -1
  565. package/src/executors/dev-doctor/dev-doctor.js.map +0 -1
  566. package/src/executors/dev-setup/dev-setup.js.map +0 -1
  567. package/src/executors/developer-context/developer-context.js.map +0 -1
  568. package/src/executors/fleet/fleet.js.map +0 -1
  569. package/src/executors/foundation-preflight/foundation-preflight.js.map +0 -1
  570. package/src/executors/foundation-profile-preflight/foundation-profile-preflight.js.map +0 -1
  571. package/src/executors/foundation-remove-preflight/foundation-remove-preflight.js.map +0 -1
  572. package/src/executors/github-bootstrap/github-bootstrap.js.map +0 -1
  573. package/src/executors/infrastructure-plan/infrastructure-plan.js.map +0 -1
  574. package/src/executors/refresh/refresh.js.map +0 -1
  575. package/src/executors/remove/remove.js.map +0 -1
  576. package/src/executors/secret-guardrails/secret-guardrails.js.map +0 -1
  577. package/src/executors/secret-metadata/secret-metadata.js.map +0 -1
  578. package/src/executors/sst-lifecycle/logging-preflight.js.map +0 -1
  579. package/src/executors/sst-lifecycle/run-sst-command.js.map +0 -1
  580. package/src/executors/sst-lifecycle/secret-preflight.js.map +0 -1
  581. package/src/executors/unlock/unlock.js.map +0 -1
  582. package/src/executors/work-markers/work-markers.js.map +0 -1
  583. package/src/foundation/discovery.js.map +0 -1
  584. package/src/foundation/index.js.map +0 -1
  585. package/src/foundation/local-database-contract.js.map +0 -1
  586. package/src/foundation/logging-preflight.js.map +0 -1
  587. package/src/foundation/logging.js.map +0 -1
  588. package/src/foundation/preflight.js.map +0 -1
  589. package/src/foundation/provision-database-bindings.js.map +0 -1
  590. package/src/foundation/provision-logical-database.js.map +0 -1
  591. package/src/foundation/requirements.js.map +0 -1
  592. package/src/foundation/router-origin-proof.js.map +0 -1
  593. package/src/foundation/stages.js.map +0 -1
  594. package/src/frontend-security/index.js.map +0 -1
  595. package/src/frontend-security/security-headers.js.map +0 -1
  596. package/src/generators/feature-registry.js.map +0 -1
  597. package/src/generators/function/function.js.map +0 -1
  598. package/src/generators/job/job.js.map +0 -1
  599. package/src/generators/logging-registry.js.map +0 -1
  600. package/src/generators/post-generation-install.js.map +0 -1
  601. package/src/generators/preset/generator.js.map +0 -1
  602. package/src/generators/preset/tool-versions.js.map +0 -1
  603. package/src/generators/preset/workspace-files-developer-agent.js.map +0 -1
  604. package/src/generators/preset/workspace-files-foundation-workload-roles.js.map +0 -1
  605. package/src/generators/preset/workspace-files-performance-cost.js.map +0 -1
  606. package/src/generators/preset/workspace-files-performance-load.js.map +0 -1
  607. package/src/generators/preset/workspace-files-workload-role-baseline.js.map +0 -1
  608. package/src/generators/preset/workspace-files.js.map +0 -1
  609. package/src/generators/repository-policy/repository-policy.js.map +0 -1
  610. package/src/generators/service/service.js.map +0 -1
  611. package/src/generators/slice/built-in-plan-api-contract-files.js.map +0 -1
  612. package/src/generators/slice/built-in-plan-api-files.js.map +0 -1
  613. package/src/generators/slice/built-in-plan-async-work-files.js.map +0 -1
  614. package/src/generators/slice/built-in-plan-authorization-files.js.map +0 -1
  615. package/src/generators/slice/built-in-plan-authorization-infrastructure-files.js.map +0 -1
  616. package/src/generators/slice/built-in-plan-data-deletion-files.js.map +0 -1
  617. package/src/generators/slice/built-in-plan-data-export-infrastructure-files.js.map +0 -1
  618. package/src/generators/slice/built-in-plan-data-files.js.map +0 -1
  619. package/src/generators/slice/built-in-plan-data-lifecycle-files.js.map +0 -1
  620. package/src/generators/slice/built-in-plan-data-lifecycle-runtime-files.js.map +0 -1
  621. package/src/generators/slice/built-in-plan-data-retention-job-files.js.map +0 -1
  622. package/src/generators/slice/built-in-plan-data-retention-launcher-files.js.map +0 -1
  623. package/src/generators/slice/built-in-plan-data-rights-files.js.map +0 -1
  624. package/src/generators/slice/built-in-plan-database-lifecycle-files.js.map +0 -1
  625. package/src/generators/slice/built-in-plan-database-retirement-rehearsal-files.js.map +0 -1
  626. package/src/generators/slice/built-in-plan-foundation-files.js.map +0 -1
  627. package/src/generators/slice/built-in-plan-frontend-files.js.map +0 -1
  628. package/src/generators/slice/built-in-plan-infrastructure-files.js.map +0 -1
  629. package/src/generators/slice/built-in-plan-infrastructure-safety-files.js.map +0 -1
  630. package/src/generators/slice/built-in-plan-metadata-files.js.map +0 -1
  631. package/src/generators/slice/built-in-plan-operations-files.js.map +0 -1
  632. package/src/generators/slice/built-in-plan-performance-files.js.map +0 -1
  633. package/src/generators/slice/built-in-plan-project-files.js.map +0 -1
  634. package/src/generators/slice/built-in-plan-release-files.js.map +0 -1
  635. package/src/generators/slice/built-in-plan-reliability-files.js.map +0 -1
  636. package/src/generators/slice/built-in-plan-secret-runtime-files.js.map +0 -1
  637. package/src/generators/slice/built-in-plan-shared.js.map +0 -1
  638. package/src/generators/slice/built-in-plan-testing-files.js.map +0 -1
  639. package/src/generators/slice/built-in-plan-verification-files.js.map +0 -1
  640. package/src/generators/slice/built-in-plan-web-files.js.map +0 -1
  641. package/src/generators/slice/built-in-plan-workload-role-files.js.map +0 -1
  642. package/src/generators/slice/built-in-plan.js.map +0 -1
  643. package/src/generators/slice/slice.js.map +0 -1
  644. package/src/generators/upgrade/upgrade.js.map +0 -1
  645. package/src/generators/workload/frozen-container.js.map +0 -1
  646. package/src/generators/workload/performance-registration.js.map +0 -1
  647. package/src/generators/workload/reliability-registration.js.map +0 -1
  648. package/src/generators/workload/service-performance-files.js.map +0 -1
  649. package/src/generators/workload/workload.js.map +0 -1
  650. package/src/git-policy/action-pins.js.map +0 -1
  651. package/src/git-policy/github-bootstrap.js.map +0 -1
  652. package/src/git-policy/index.js.map +0 -1
  653. package/src/git-policy/routing.js.map +0 -1
  654. package/src/index.js.map +0 -1
  655. package/src/infrastructure-change/convergence.js.map +0 -1
  656. package/src/infrastructure-change/drift.js.map +0 -1
  657. package/src/infrastructure-change/index.js.map +0 -1
  658. package/src/infrastructure-change/model.js.map +0 -1
  659. package/src/infrastructure-change/normalize.js.map +0 -1
  660. package/src/infrastructure-change/plan.js.map +0 -1
  661. package/src/infrastructure-change/risk.js.map +0 -1
  662. package/src/infrastructure-change/summary.js.map +0 -1
  663. package/src/local-dev/context-discovery.js.map +0 -1
  664. package/src/local-dev/contract-validation.js.map +0 -1
  665. package/src/local-dev/contracts.js.map +0 -1
  666. package/src/local-dev/control-contract.js.map +0 -1
  667. package/src/local-dev/dev-command-core.js.map +0 -1
  668. package/src/local-dev/effects.js.map +0 -1
  669. package/src/local-dev/environment-inspector.js.map +0 -1
  670. package/src/local-dev/environment-state.js.map +0 -1
  671. package/src/local-dev/errors.js.map +0 -1
  672. package/src/local-dev/fleet-client.js.map +0 -1
  673. package/src/local-dev/fleet-configuration.js.map +0 -1
  674. package/src/local-dev/fleet-inventory.js.map +0 -1
  675. package/src/local-dev/fleet-ipc.js.map +0 -1
  676. package/src/local-dev/fleet-launcher.js.map +0 -1
  677. package/src/local-dev/fleet-lock.js.map +0 -1
  678. package/src/local-dev/fleet-logs.js.map +0 -1
  679. package/src/local-dev/fleet-process.js.map +0 -1
  680. package/src/local-dev/fleet-runtime.js.map +0 -1
  681. package/src/local-dev/fleet-session.js.map +0 -1
  682. package/src/local-dev/fleet-slice-runner-daemon.js.map +0 -1
  683. package/src/local-dev/fleet-startup.js.map +0 -1
  684. package/src/local-dev/fleet-state.js.map +0 -1
  685. package/src/local-dev/fleet-supervisor-daemon.js.map +0 -1
  686. package/src/local-dev/fleet-supervisor.js.map +0 -1
  687. package/src/local-dev/gateway-daemon.js.map +0 -1
  688. package/src/local-dev/gateway.js.map +0 -1
  689. package/src/local-dev/identity.js.map +0 -1
  690. package/src/local-dev/index.js.map +0 -1
  691. package/src/local-dev/local-context.js.map +0 -1
  692. package/src/local-dev/nx-workspace.js.map +0 -1
  693. package/src/local-dev/personal-secret-ledger.js.map +0 -1
  694. package/src/local-dev/personal-stage.js.map +0 -1
  695. package/src/local-dev/portless.js.map +0 -1
  696. package/src/local-dev/ports.js.map +0 -1
  697. package/src/local-dev/private-state.js.map +0 -1
  698. package/src/local-dev/process-identity.js.map +0 -1
  699. package/src/local-dev/proxy.js.map +0 -1
  700. package/src/local-dev/runtime-paths.js.map +0 -1
  701. package/src/local-dev/secret-slice-claim.js.map +0 -1
  702. package/src/local-dev/slice-claim.js.map +0 -1
  703. package/src/local-dev/slice-runner.js.map +0 -1
  704. package/src/local-dev/tls.js.map +0 -1
  705. package/src/ownership/composition.js.map +0 -1
  706. package/src/ownership/foundation-record.js.map +0 -1
  707. package/src/ownership/hash.js.map +0 -1
  708. package/src/ownership/index.js.map +0 -1
  709. package/src/ownership/json-ownership.js.map +0 -1
  710. package/src/ownership/record.js.map +0 -1
  711. package/src/ownership/workspace-record.js.map +0 -1
  712. package/src/performance/cost-collection.js.map +0 -1
  713. package/src/performance/cost-controls.js.map +0 -1
  714. package/src/performance/evidence.js.map +0 -1
  715. package/src/performance/index.js.map +0 -1
  716. package/src/performance/load-safety.js.map +0 -1
  717. package/src/performance/model.js.map +0 -1
  718. package/src/performance/pr-artifact-budget.js.map +0 -1
  719. package/src/performance/runner.js.map +0 -1
  720. package/src/performance/scenario-endpoint.js.map +0 -1
  721. package/src/performance/scenario.js.map +0 -1
  722. package/src/performance/test-fixtures.d.ts +0 -7
  723. package/src/performance/test-fixtures.js +0 -169
  724. package/src/performance/test-fixtures.js.map +0 -1
  725. package/src/performance/unit-cost.js.map +0 -1
  726. package/src/release/index.js.map +0 -1
  727. package/src/release/lifecycle.js.map +0 -1
  728. package/src/release/manifest.js.map +0 -1
  729. package/src/release/rollout.js.map +0 -1
  730. package/src/release/source-authority.js.map +0 -1
  731. package/src/repository-policy/aurora-preflight.js.map +0 -1
  732. package/src/repository-policy/deployment-plan.js.map +0 -1
  733. package/src/repository-policy/foundation-profile.js.map +0 -1
  734. package/src/repository-policy/index.js.map +0 -1
  735. package/src/repository-policy/policy.js.map +0 -1
  736. package/src/secrets/adapters.js.map +0 -1
  737. package/src/secrets/aws-control-plane.js.map +0 -1
  738. package/src/secrets/declaration-verification.js.map +0 -1
  739. package/src/secrets/guardrails.js.map +0 -1
  740. package/src/secrets/index.js.map +0 -1
  741. package/src/secrets/metadata.js.map +0 -1
  742. package/src/secrets/model.js.map +0 -1
  743. package/src/secrets/operator.js.map +0 -1
  744. package/src/secrets/permissions.js.map +0 -1
  745. package/src/secrets/personal-command.js.map +0 -1
  746. package/src/secrets/personal-ledger.js.map +0 -1
  747. package/src/secrets/registry.js.map +0 -1
  748. package/src/secrets/verification.js.map +0 -1
  749. package/src/secrets/workload-access.js.map +0 -1
  750. package/src/supply-chain/action-pins.js.map +0 -1
  751. package/src/supply-chain/blueprint-trust-git.js.map +0 -1
  752. package/src/supply-chain/blueprint-trust-process.js.map +0 -1
  753. package/src/supply-chain/blueprint-trust.js.map +0 -1
  754. package/src/supply-chain/blueprint-verification-authority.js.map +0 -1
  755. package/src/supply-chain/blueprint-verification-execution.js.map +0 -1
  756. package/src/supply-chain/build-output.js.map +0 -1
  757. package/src/supply-chain/common.js.map +0 -1
  758. package/src/supply-chain/container-artifact-contract.js.map +0 -1
  759. package/src/supply-chain/container-release.js.map +0 -1
  760. package/src/supply-chain/ecr-applicability.js.map +0 -1
  761. package/src/supply-chain/ecr-scanning.js.map +0 -1
  762. package/src/supply-chain/ecs-deployed-image.js.map +0 -1
  763. package/src/supply-chain/errors.js.map +0 -1
  764. package/src/supply-chain/generated-blueprint-trust-command.js.map +0 -1
  765. package/src/supply-chain/generated-command.js.map +0 -1
  766. package/src/supply-chain/generated-container-release-command.js.map +0 -1
  767. package/src/supply-chain/generated-ecr-collector-command.js.map +0 -1
  768. package/src/supply-chain/generated-files.js.map +0 -1
  769. package/src/supply-chain/generated-scanner-command.js.map +0 -1
  770. package/src/supply-chain/index.js.map +0 -1
  771. package/src/supply-chain/inventory.js.map +0 -1
  772. package/src/supply-chain/license-expression.js.map +0 -1
  773. package/src/supply-chain/pnpm-toolchain-authority.js.map +0 -1
  774. package/src/supply-chain/policy.js.map +0 -1
  775. package/src/supply-chain/publication.js.map +0 -1
  776. package/src/supply-chain/sbom.js.map +0 -1
  777. package/src/supply-chain/scanner.js.map +0 -1
  778. package/src/supply-chain/vendored-source.js.map +0 -1
  779. package/src/testing-evidence/fixture-scope.js.map +0 -1
  780. package/src/testing-evidence/index.js.map +0 -1
  781. package/src/testing-evidence/quarantine.js.map +0 -1
  782. package/src/testing-evidence/result.js.map +0 -1
  783. package/src/testing-evidence/runner-adapter.js.map +0 -1
  784. package/src/testing-evidence/sensitive-artifacts.js.map +0 -1
  785. package/src/validation.js.map +0 -1
  786. package/src/work-markers/index.js.map +0 -1
  787. package/src/work-markers/work-markers.js.map +0 -1
  788. package/src/workload-roles/account-baseline.js.map +0 -1
  789. package/src/workload-roles/authority.js.map +0 -1
  790. package/src/workload-roles/aws-adapter.js.map +0 -1
  791. package/src/workload-roles/aws-internal-handler.js.map +0 -1
  792. package/src/workload-roles/aws-s3-state.js.map +0 -1
  793. package/src/workload-roles/aws-sdk-control-plane.js.map +0 -1
  794. package/src/workload-roles/boundary.js.map +0 -1
  795. package/src/workload-roles/convergence.js.map +0 -1
  796. package/src/workload-roles/effective-policy.js.map +0 -1
  797. package/src/workload-roles/handler.js.map +0 -1
  798. package/src/workload-roles/history.js.map +0 -1
  799. package/src/workload-roles/index.js.map +0 -1
  800. package/src/workload-roles/injection.js.map +0 -1
  801. package/src/workload-roles/manifest.js.map +0 -1
  802. package/src/workload-roles/provisioner.js.map +0 -1
  803. package/src/workload-roles/registration.js.map +0 -1
  804. package/src/workload-roles/stage-boundary.js.map +0 -1
  805. package/src/workload-roles/standard-slice.js.map +0 -1
@@ -5,7 +5,75 @@ exports.renderContainerReleaseCommand = renderContainerReleaseCommand;
5
5
  const generated_ecr_collector_command_js_1 = require("./generated-ecr-collector-command.js");
6
6
  const safe_reference_1 = require("@empire-builder-kit/runtime/safe-reference");
7
7
  exports.CRANE_VERSION = '0.21.7';
8
+ const WINDOWS_HARDEN_PRIVATE_ACL = String.raw `
9
+ $ErrorActionPreference = 'Stop'
10
+ $path = $env:EBK_PRIVATE_PATH
11
+ $kind = $env:EBK_PRIVATE_KIND
12
+ $identity = [System.Security.Principal.WindowsIdentity]::GetCurrent()
13
+ if ($kind -eq 'directory') {
14
+ $info = [System.IO.DirectoryInfo]::new($path)
15
+ } else {
16
+ $info = [System.IO.FileInfo]::new($path)
17
+ }
18
+ $acl = $info.GetAccessControl()
19
+ $acl.SetAccessRuleProtection($true, $false)
20
+ foreach ($rule in @($acl.Access)) {
21
+ [void]$acl.RemoveAccessRuleSpecific($rule)
22
+ }
23
+ $rights = [System.Security.AccessControl.FileSystemRights]::FullControl
24
+ $type = [System.Security.AccessControl.AccessControlType]::Allow
25
+ if ($kind -eq 'directory') {
26
+ $inheritance = [System.Security.AccessControl.InheritanceFlags]'ContainerInherit,ObjectInherit'
27
+ $propagation = [System.Security.AccessControl.PropagationFlags]::None
28
+ $rule = [System.Security.AccessControl.FileSystemAccessRule]::new(
29
+ $identity.User, $rights, $inheritance, $propagation, $type
30
+ )
31
+ } else {
32
+ $rule = [System.Security.AccessControl.FileSystemAccessRule]::new(
33
+ $identity.User, $rights, $type
34
+ )
35
+ }
36
+ $acl.AddAccessRule($rule)
37
+ $info.SetAccessControl($acl)
38
+ `;
39
+ const WINDOWS_ASSERT_PRIVATE_ACL = String.raw `
40
+ $ErrorActionPreference = 'Stop'
41
+ $path = $env:EBK_PRIVATE_PATH
42
+ $kind = $env:EBK_PRIVATE_KIND
43
+ $identity = [System.Security.Principal.WindowsIdentity]::GetCurrent()
44
+ if ($kind -eq 'directory') {
45
+ $info = [System.IO.DirectoryInfo]::new($path)
46
+ } else {
47
+ $info = [System.IO.FileInfo]::new($path)
48
+ }
49
+ $acl = $info.GetAccessControl()
50
+ if (-not $acl.AreAccessRulesProtected) {
51
+ throw 'ACL inheritance is enabled.'
52
+ }
53
+ $allowRules = @($acl.Access | Where-Object {
54
+ $_.AccessControlType -eq [System.Security.AccessControl.AccessControlType]::Allow
55
+ })
56
+ $unsafeRules = @($allowRules | Where-Object {
57
+ $_.IdentityReference.Translate([System.Security.Principal.SecurityIdentifier]).Value -ne $identity.User.Value
58
+ })
59
+ $selfRules = @($allowRules | Where-Object {
60
+ $_.IdentityReference.Translate([System.Security.Principal.SecurityIdentifier]).Value -eq $identity.User.Value
61
+ })
62
+ if ($unsafeRules.Count -ne 0 -or $selfRules.Count -eq 0) {
63
+ throw 'ACL grants access beyond the current user or omits the current user.'
64
+ }
65
+ `;
8
66
  exports.CRANE_RELEASES = Object.freeze({
67
+ 'darwin-arm64': {
68
+ archiveFormat: 'tar-gzip',
69
+ sha256: '1858c55dcd6053fe869bcb0c4ec20666383ddce445ad0f7e15e1e506b1f7fe52',
70
+ source: 'https://github.com/google/go-containerregistry/releases/download/v0.21.7/go-containerregistry_Darwin_arm64.tar.gz',
71
+ },
72
+ 'darwin-x64': {
73
+ archiveFormat: 'tar-gzip',
74
+ sha256: '63a7dd15168d4dcac37933c7f6745438f2943d5898a1cf7896ad3341d8519bf2',
75
+ source: 'https://github.com/google/go-containerregistry/releases/download/v0.21.7/go-containerregistry_Darwin_x86_64.tar.gz',
76
+ },
9
77
  'linux-arm64': {
10
78
  archiveFormat: 'tar-gzip',
11
79
  sha256: 'b6ee979d9411dfb05ce35ab9e156fe5de7def11a230764a7856ffa2eb971fa88',
@@ -19,7 +87,7 @@ exports.CRANE_RELEASES = Object.freeze({
19
87
  });
20
88
  function validatedReleases(releases) {
21
89
  if (Object.keys(releases).length === 0 ||
22
- Object.entries(releases).some(([platform, release]) => !/^linux-(?:arm64|x64)$/.test(platform) ||
90
+ Object.entries(releases).some(([platform, release]) => !/^(?:darwin|linux)-(?:arm64|x64)$/.test(platform) ||
23
91
  !['binary', 'tar-gzip'].includes(release.archiveFormat) ||
24
92
  !/^[a-f0-9]{64}$/.test(release.sha256) ||
25
93
  !/^https:\/\/github\.com\/anchore\/syft\/releases\/download\/v\d+\.\d+\.\d+\/[A-Za-z0-9._-]+$/.test(release.source))) {
@@ -29,10 +97,10 @@ function validatedReleases(releases) {
29
97
  }
30
98
  function validatedCraneReleases(releases) {
31
99
  if (Object.keys(releases).length === 0 ||
32
- Object.entries(releases).some(([platform, release]) => !/^linux-(?:arm64|x64)$/.test(platform) ||
100
+ Object.entries(releases).some(([platform, release]) => !/^(?:darwin|linux)-(?:arm64|x64)$/.test(platform) ||
33
101
  release.archiveFormat !== 'tar-gzip' ||
34
102
  !/^[a-f0-9]{64}$/.test(release.sha256) ||
35
- !/^https:\/\/github\.com\/google\/go-containerregistry\/releases\/download\/v\d+\.\d+\.\d+\/go-containerregistry_Linux_(?:arm64|x86_64)\.tar\.gz$/.test(release.source))) {
103
+ !/^https:\/\/github\.com\/google\/go-containerregistry\/releases\/download\/v\d+\.\d+\.\d+\/go-containerregistry_(?:Darwin|Linux)_(?:arm64|x86_64)\.tar\.gz$/.test(release.source))) {
36
104
  throw new Error('Container-release Crane assets must be exact official integrity-bound releases.');
37
105
  }
38
106
  return releases;
@@ -49,10 +117,11 @@ function renderContainerReleaseCommand(options = {}) {
49
117
  !/^\d+\.\d+\.\d+$/.test(craneVersion)) {
50
118
  throw new Error('The generated container release configuration is invalid.');
51
119
  }
52
- return `import { createHash } from 'node:crypto';
53
- import { execFileSync, spawnSync } from 'node:child_process';
120
+ return `import { createHash, randomBytes } from 'node:crypto';
121
+ import { execFileSync, spawn, spawnSync } from 'node:child_process';
54
122
  import {
55
123
  chmodSync,
124
+ copyFileSync,
56
125
  existsSync,
57
126
  lstatSync,
58
127
  mkdirSync,
@@ -60,11 +129,13 @@ import {
60
129
  readFileSync,
61
130
  realpathSync,
62
131
  readdirSync,
132
+ renameSync,
63
133
  rmSync,
134
+ unlinkSync,
64
135
  writeFileSync,
65
136
  } from 'node:fs';
66
137
  import { basename, isAbsolute, join, relative, resolve, sep } from 'node:path';
67
- import { tmpdir } from 'node:os';
138
+ import { platform as hostOperatingSystem, tmpdir } from 'node:os';
68
139
  import {
69
140
  CONTAINER_NATIVE_LIFECYCLE_PROOFS,
70
141
  CONTAINER_RELEASE_PLATFORMS,
@@ -72,6 +143,7 @@ import {
72
143
  createContainerNativeRunProof,
73
144
  createContainerPlatformEvidence,
74
145
  createContainerReleaseBinding,
146
+ hasSensitiveSupplyChainShape,
75
147
  resolveContainerReleaseArtifact,
76
148
  supplyChainDigest,
77
149
  validateContainerArtifactIdentity,
@@ -85,6 +157,8 @@ const SYFT_VERSION = ${JSON.stringify(syftVersion)};
85
157
  const SYFT_RELEASES = Object.freeze(${JSON.stringify(releases)});
86
158
  const CRANE_VERSION = ${JSON.stringify(craneVersion)};
87
159
  const CRANE_RELEASES = Object.freeze(${JSON.stringify(craneReleases)});
160
+ const WINDOWS_HARDEN_PRIVATE_ACL = ${JSON.stringify(WINDOWS_HARDEN_PRIVATE_ACL)};
161
+ const WINDOWS_ASSERT_PRIVATE_ACL = ${JSON.stringify(WINDOWS_ASSERT_PRIVATE_ACL)};
88
162
  const SHA256 = /^[a-f0-9]{64}$/;
89
163
  const IMAGE_DIGEST = /^sha256:[a-f0-9]{64}$/;
90
164
  const COMMIT = /^(?:[a-f0-9]{40}|[a-f0-9]{64})$/;
@@ -95,6 +169,39 @@ const AWS_REGION_NAME =
95
169
  /^(?=.{9,32}$)[a-z]{2}(?:-gov)?-[a-z]+(?:-[a-z]+)*-\\d$/;
96
170
  const MAX_TOOL_BYTES = 100 * 1024 * 1024;
97
171
  const MAX_OUTPUT_BYTES = 64 * 1024 * 1024;
172
+ // Optional absolute cache roots supplied by the workflow (runner-local, restored
173
+ // by the managed cache step). Absent or relative values disable the caches.
174
+ const optionalCacheRoot = (name) => {
175
+ const value = process.env[name]?.trim() ?? '';
176
+ return value !== '' && isAbsolute(value) && !value.includes('..') ? value : '';
177
+ };
178
+ const buildxLayerCache = (repositoryName, architecture) => {
179
+ const root = optionalCacheRoot('EBK_BUILDX_CACHE_DIR');
180
+ if (root === '') return { arguments: [], commit: () => {} };
181
+ const scope = repositoryName.replaceAll('/', '-') + '-' + architecture;
182
+ const current = join(root, scope);
183
+ const next = join(root, scope + '.next');
184
+ mkdirSync(root, { mode: 0o700, recursive: true });
185
+ rmSync(next, { force: true, recursive: true });
186
+ return {
187
+ arguments: [
188
+ ...(existsSync(current)
189
+ ? ['--cache-from', 'type=local,src=' + current]
190
+ : []),
191
+ '--cache-to',
192
+ 'type=local,dest=' + next + ',mode=max',
193
+ ],
194
+ commit: () => {
195
+ if (!existsSync(next)) return;
196
+ rmSync(current, { force: true, recursive: true });
197
+ renameSync(next, current);
198
+ },
199
+ };
200
+ };
201
+ const cachedReleaseToolArchive = (name, sha256) => {
202
+ const root = optionalCacheRoot('EBK_RELEASE_TOOL_CACHE_DIR');
203
+ return root === '' ? '' : join(root, name + '-' + sha256);
204
+ };
98
205
  const mode = process.argv[2];
99
206
  const requestedPlatform = process.argv[3];
100
207
  if (
@@ -129,23 +236,102 @@ const sourceCommit =
129
236
  const stage = process.env.EBK_STAGE?.trim() ?? '';
130
237
  const accountId = process.env.AWS_EXPECTED_ACCOUNT_ID?.trim() ?? '';
131
238
  const region = process.env.AWS_REGION?.trim() ?? '';
132
- const runId = process.env.GITHUB_RUN_ID?.trim() ?? '';
133
- const runAttempt = process.env.GITHUB_RUN_ATTEMPT?.trim() ?? '';
134
- const workflow = process.env.GITHUB_WORKFLOW_REF?.trim() ?? '';
239
+ const packageRegistryInput =
240
+ process.env.EBK_CONTAINER_PACKAGE_REGISTRY?.trim() ?? '';
241
+ const localFederated =
242
+ process.env.GITHUB_ACTIONS !== 'true' &&
243
+ process.env.EBK_LOCAL_DEPLOYMENT_MODE === 'federated';
244
+ const localRunId = process.env.EBK_LOCAL_CONTAINER_RUN_ID?.trim() ?? '';
245
+ const policy = JSON.parse(
246
+ readFileSync(join(repositoryRoot, 'ebk.repository-policy.json'), 'utf8'),
247
+ );
248
+ const selectedDeploymentProfile =
249
+ process.env.EBK_DEPLOYMENT_PROFILE?.trim() ?? '';
250
+ const activeStages = {
251
+ starter: ['development', 'production'],
252
+ 'production-isolated': ['development', 'staging', 'production'],
253
+ 'fully-isolated': ['development', 'staging', 'production'],
254
+ }[policy.deploymentProfile];
255
+ const locallyAdmittedStages = {
256
+ starter: ['development', 'production'],
257
+ 'production-isolated': ['development', 'staging'],
258
+ 'fully-isolated': ['development'],
259
+ }[policy.deploymentProfile];
260
+ if (
261
+ selectedDeploymentProfile !== policy.deploymentProfile ||
262
+ !activeStages ||
263
+ !activeStages.includes(stage) ||
264
+ policy.deploymentAccounts?.[stage] !== accountId ||
265
+ policy.deploymentRegion !== region
266
+ ) {
267
+ throw new Error(
268
+ 'Container release requires the exact committed profile, active stage, account, and Region.',
269
+ );
270
+ }
271
+ if (
272
+ localFederated &&
273
+ (!locallyAdmittedStages || !locallyAdmittedStages.includes(stage))
274
+ ) {
275
+ throw new Error(
276
+ 'Local container release requires one of the committed profile locally admitted stages.',
277
+ );
278
+ }
279
+ const runId = localFederated
280
+ ? localRunId
281
+ : process.env.GITHUB_RUN_ID?.trim() ?? '';
282
+ const runAttempt = localFederated
283
+ ? '1'
284
+ : process.env.GITHUB_RUN_ATTEMPT?.trim() ?? '';
285
+ const workflow = localFederated
286
+ ? 'local-federated/' + stage + '/' + accountId + '/' + region
287
+ : process.env.GITHUB_WORKFLOW_REF?.trim() ?? '';
135
288
  if (
136
289
  !COMMIT.test(deploymentSourceCommit) ||
137
290
  !COMMIT.test(sourceCommit) ||
138
291
  !['development', 'staging', 'production'].includes(stage) ||
139
292
  !/^\\d{12}$/.test(accountId) ||
140
293
  !AWS_REGION_NAME.test(region) ||
294
+ (!localFederated && process.env.GITHUB_ACTIONS !== 'true') ||
141
295
  !/^[1-9]\\d*$/.test(runId) ||
142
296
  !/^[1-9]\\d*$/.test(runAttempt) ||
143
297
  !REFERENCE.test(workflow)
144
298
  ) {
145
299
  throw new Error(
146
- 'Container release requires one exact protected source, run, account, Region, and stage.',
300
+ 'Container release requires one exact GitHub or local-federated source, run, account, Region, and stage.',
147
301
  );
148
302
  }
303
+ const packageRegistry = (() => {
304
+ if (packageRegistryInput === '') return undefined;
305
+ if (packageRegistryInput.length > 2_048) {
306
+ throw new Error('Container package registry authority is invalid.');
307
+ }
308
+ let parsed;
309
+ try {
310
+ parsed = new URL(packageRegistryInput);
311
+ } catch {
312
+ throw new Error('Container package registry authority is invalid.');
313
+ }
314
+ const normalized = parsed.href;
315
+ const canonicalInput =
316
+ packageRegistryInput === normalized ||
317
+ packageRegistryInput + '/' === normalized;
318
+ const localPlaintext =
319
+ parsed.protocol === 'http:' &&
320
+ ['127.0.0.1', 'localhost', 'host.docker.internal', '[::1]'].includes(
321
+ parsed.hostname,
322
+ );
323
+ if (
324
+ !canonicalInput ||
325
+ (parsed.protocol !== 'https:' && !localPlaintext) ||
326
+ parsed.username !== '' ||
327
+ parsed.password !== '' ||
328
+ parsed.search !== '' ||
329
+ parsed.hash !== ''
330
+ ) {
331
+ throw new Error('Container package registry authority is invalid.');
332
+ }
333
+ return normalized.endsWith('/') ? normalized.slice(0, -1) : normalized;
334
+ })();
149
335
  const requireReviewedCheckout = () => {
150
336
  validateReviewedReleaseCheckout({
151
337
  deploymentSourceCommit,
@@ -290,12 +476,657 @@ const execute = (command, args, label, options = {}) => {
290
476
  stdio: ['pipe', 'pipe', 'pipe'],
291
477
  });
292
478
  } catch (error) {
479
+ if (options.failureDiagnostics === 'bounded-closure') {
480
+ const diagnosticStream = (value) => {
481
+ const raw = value === undefined || value === null ? '' : String(value);
482
+ if (raw.length === 0) return '[empty]';
483
+ if (hasSensitiveSupplyChainShape(raw)) {
484
+ return '[redacted-sensitive-output]';
485
+ }
486
+ const bytes = Buffer.from(raw, 'utf8');
487
+ const truncated = bytes.byteLength > 1_024;
488
+ const tail = truncated
489
+ ? bytes.subarray(bytes.byteLength - 1_024).toString('utf8')
490
+ : raw;
491
+ return JSON.stringify((truncated ? '[truncated]\\n' : '') + tail);
492
+ };
493
+ const status = Number.isSafeInteger(error?.status)
494
+ ? String(error.status)
495
+ : 'unavailable';
496
+ const signal =
497
+ typeof error?.signal === 'string' && /^[A-Z0-9]+$/.test(error.signal)
498
+ ? error.signal
499
+ : 'none';
500
+ throw new Error(
501
+ label +
502
+ ' failed without complete evidence: ' +
503
+ 'status=' +
504
+ status +
505
+ '; signal=' +
506
+ signal +
507
+ '; stdout=' +
508
+ diagnosticStream(error?.stdout) +
509
+ '; stderr=' +
510
+ diagnosticStream(error?.stderr),
511
+ );
512
+ }
293
513
  throw new Error(
294
514
  label + ' failed without complete evidence: ' +
295
515
  String(error?.stderr ?? error?.message ?? error).slice(0, 2_000),
296
516
  );
297
517
  }
298
518
  };
519
+ let releaseBuilder;
520
+ let releaseBuildChild;
521
+ let releaseBuildStopPromise;
522
+ let releaseBuilderSignal;
523
+ const releaseBuilderSignalHandlers = new Map();
524
+ const releaseToolTemporaryDirectories = new Set();
525
+ const yieldToReleaseSignals = () =>
526
+ new Promise((resolve) => setImmediate(resolve));
527
+ const stopExactReleaseBuildChild = async (signal = 'SIGTERM') => {
528
+ if (releaseBuildStopPromise) return releaseBuildStopPromise;
529
+ const observed = releaseBuildChild;
530
+ if (!observed) return;
531
+ releaseBuildStopPromise = (async () => {
532
+ const child = observed.child;
533
+ if (hostOperatingSystem() === 'win32') {
534
+ const taskkill = join(process.env.SystemRoot ?? '', 'System32', 'taskkill.exe');
535
+ if (!isAbsolute(taskkill) || !existsSync(taskkill) || realpathSync(taskkill) !== taskkill) {
536
+ throw new Error('Exact Windows Buildx cleanup requires canonical SystemRoot.');
537
+ }
538
+ const stopped = spawnSync(
539
+ taskkill,
540
+ ['/PID', String(child.pid), '/T', '/F'],
541
+ {
542
+ cwd: repositoryRoot,
543
+ encoding: 'utf8',
544
+ env: process.env,
545
+ maxBuffer: 1024 * 1024,
546
+ stdio: ['ignore', 'pipe', 'pipe'],
547
+ windowsHide: true,
548
+ },
549
+ );
550
+ if (
551
+ child.exitCode === null &&
552
+ (stopped.error || stopped.signal || stopped.status !== 0)
553
+ ) {
554
+ throw new Error('Exact Windows Buildx process-tree cleanup failed.');
555
+ }
556
+ } else if (child.pid) {
557
+ try {
558
+ process.kill(-child.pid, signal);
559
+ } catch (error) {
560
+ if (error?.code !== 'ESRCH') throw error;
561
+ }
562
+ }
563
+ if (child.exitCode === null) {
564
+ const closed = await Promise.race([
565
+ observed.close.then(() => true),
566
+ new Promise((resolve) => setTimeout(() => resolve(false), 5_000)),
567
+ ]);
568
+ if (!closed && hostOperatingSystem() !== 'win32' && child.pid) {
569
+ try {
570
+ process.kill(-child.pid, 'SIGKILL');
571
+ } catch (error) {
572
+ if (error?.code !== 'ESRCH') throw error;
573
+ }
574
+ const killed = await Promise.race([
575
+ observed.close.then(() => true),
576
+ new Promise((resolve) => setTimeout(() => resolve(false), 5_000)),
577
+ ]);
578
+ if (!killed) {
579
+ throw new Error('Exact Buildx process group did not terminate.');
580
+ }
581
+ }
582
+ }
583
+ })().finally(() => {
584
+ if (releaseBuildChild === observed) releaseBuildChild = undefined;
585
+ releaseBuildStopPromise = undefined;
586
+ });
587
+ return releaseBuildStopPromise;
588
+ };
589
+ const executeObservedBuildx = async (args, label) => {
590
+ if (releaseBuildChild) {
591
+ throw new Error('Only one invocation-owned Buildx child may run at a time.');
592
+ }
593
+ const child = spawn('docker', args, {
594
+ cwd: repositoryRoot,
595
+ detached: hostOperatingSystem() !== 'win32',
596
+ env: process.env,
597
+ stdio: ['ignore', 'pipe', 'pipe'],
598
+ windowsHide: true,
599
+ });
600
+ const stdout = [];
601
+ const stderr = [];
602
+ let outputBytes = 0;
603
+ const retain = (chunks, stream, chunk) => {
604
+ outputBytes += chunk.byteLength;
605
+ if (outputBytes > MAX_OUTPUT_BYTES) {
606
+ void stopExactReleaseBuildChild('SIGTERM');
607
+ return;
608
+ }
609
+ chunks.push(chunk);
610
+ stream.write(chunk);
611
+ };
612
+ child.stdout.on('data', (chunk) => retain(stdout, process.stdout, chunk));
613
+ child.stderr.on('data', (chunk) => retain(stderr, process.stderr, chunk));
614
+ const close = new Promise((resolve) => {
615
+ child.once('error', (error) => resolve({ error }));
616
+ child.once('close', (status, signal) => resolve({ signal, status }));
617
+ });
618
+ releaseBuildChild = { child, close };
619
+ const result = await close;
620
+ await stopExactReleaseBuildChild();
621
+ if (
622
+ outputBytes > MAX_OUTPUT_BYTES ||
623
+ result.error ||
624
+ result.signal ||
625
+ result.status !== 0
626
+ ) {
627
+ throw new Error(
628
+ label + ' failed without complete evidence: ' +
629
+ Buffer.concat(stderr).toString('utf8').slice(-2_000),
630
+ );
631
+ }
632
+ return Buffer.concat(stdout).toString('utf8');
633
+ };
634
+ const runWindowsPrivateAcl = (script, path, kind) => {
635
+ const systemRoot = process.env.SystemRoot;
636
+ if (!systemRoot || !isAbsolute(systemRoot)) {
637
+ throw new Error('Windows private-path hardening requires an absolute SystemRoot.');
638
+ }
639
+ const powershell = join(
640
+ systemRoot,
641
+ 'System32',
642
+ 'WindowsPowerShell',
643
+ 'v1.0',
644
+ 'powershell.exe',
645
+ );
646
+ const result = spawnSync(
647
+ powershell,
648
+ [
649
+ '-NoLogo',
650
+ '-NoProfile',
651
+ '-NonInteractive',
652
+ '-EncodedCommand',
653
+ Buffer.from(script, 'utf16le').toString('base64'),
654
+ ],
655
+ {
656
+ cwd: repositoryRoot,
657
+ encoding: 'utf8',
658
+ env: {
659
+ ...process.env,
660
+ EBK_PRIVATE_KIND: kind,
661
+ EBK_PRIVATE_PATH: path,
662
+ },
663
+ maxBuffer: 1024 * 1024,
664
+ stdio: ['ignore', 'pipe', 'pipe'],
665
+ timeout: 15_000,
666
+ windowsHide: true,
667
+ },
668
+ );
669
+ if (result.error || result.signal || result.status !== 0) {
670
+ throw new Error(
671
+ 'Windows private-path ACL operation failed closed: ' +
672
+ String(result.stderr || result.error?.message || result.signal || result.status)
673
+ .slice(-2_000),
674
+ );
675
+ }
676
+ };
677
+ const assertPrivateReleaseBuilderPathIdentity = (path, kind) => {
678
+ const metadata = lstatSync(path);
679
+ if (
680
+ metadata.isSymbolicLink() ||
681
+ (kind === 'directory' ? !metadata.isDirectory() : !metadata.isFile()) ||
682
+ realpathSync(path) !== path
683
+ ) {
684
+ throw new Error(
685
+ 'Container-release builder ownership path failed canonical type validation.',
686
+ );
687
+ }
688
+ return metadata;
689
+ };
690
+ const assertPrivateReleaseBuilderPath = (path, kind) => {
691
+ const metadata = assertPrivateReleaseBuilderPathIdentity(path, kind);
692
+ if (hostOperatingSystem() === 'win32') {
693
+ runWindowsPrivateAcl(WINDOWS_ASSERT_PRIVATE_ACL, path, kind);
694
+ return metadata;
695
+ }
696
+ const expectedMode = kind === 'directory' ? 0o700 : 0o600;
697
+ if (
698
+ (metadata.mode & 0o777) !== expectedMode ||
699
+ (typeof process.getuid === 'function' && metadata.uid !== process.getuid())
700
+ ) {
701
+ throw new Error(
702
+ 'Container-release builder ownership path is not current-user private.',
703
+ );
704
+ }
705
+ return metadata;
706
+ };
707
+ const hardenPrivateReleaseBuilderPath = (path, kind) => {
708
+ assertPrivateReleaseBuilderPathIdentity(path, kind);
709
+ if (hostOperatingSystem() === 'win32') {
710
+ runWindowsPrivateAcl(WINDOWS_HARDEN_PRIVATE_ACL, path, kind);
711
+ } else {
712
+ chmodSync(path, kind === 'directory' ? 0o700 : 0o600);
713
+ }
714
+ return assertPrivateReleaseBuilderPath(path, kind);
715
+ };
716
+ const assertCanonicalAncestorsBeforeCreate = (requested) => {
717
+ const requestedRelative = relative(repositoryRoot, requested);
718
+ if (
719
+ requestedRelative === '' ||
720
+ requestedRelative === '..' ||
721
+ requestedRelative.startsWith('..' + sep) ||
722
+ isAbsolute(requestedRelative)
723
+ ) {
724
+ throw new Error('Container-release builder ownership path escapes its repository.');
725
+ }
726
+ let cursor = repositoryRoot;
727
+ for (const segment of requestedRelative.split(sep)) {
728
+ cursor = join(cursor, segment);
729
+ if (!existsSync(cursor)) break;
730
+ const metadata = lstatSync(cursor);
731
+ if (metadata.isSymbolicLink() || realpathSync(cursor) !== cursor) {
732
+ throw new Error(
733
+ 'Container-release builder ownership path has a symbolic or noncanonical ancestor.',
734
+ );
735
+ }
736
+ }
737
+ };
738
+ const releaseBuilderLeaseRoot = () => {
739
+ const requested = resolve(
740
+ repositoryRoot,
741
+ '.ebk/cache/container-release-builders',
742
+ );
743
+ assertCanonicalAncestorsBeforeCreate(requested);
744
+ mkdirSync(requested, { mode: 0o700, recursive: true });
745
+ hardenPrivateReleaseBuilderPath(requested, 'directory');
746
+ const canonical = realpathSync(requested);
747
+ if (canonical !== requested) {
748
+ throw new Error(
749
+ 'Container-release builder leases require one repository-private owned directory.',
750
+ );
751
+ }
752
+ return canonical;
753
+ };
754
+ const readReleaseBuilderLease = (leasePath, expectedName) => {
755
+ const metadata = assertPrivateReleaseBuilderPath(leasePath, 'file');
756
+ if (
757
+ metadata.nlink !== 1 ||
758
+ metadata.size < 2 ||
759
+ metadata.size > 16 * 1024
760
+ ) {
761
+ throw new Error('Container-release builder lease is not one private file.');
762
+ }
763
+ let lease;
764
+ try {
765
+ lease = JSON.parse(readFileSync(leasePath, 'utf8'));
766
+ } catch {
767
+ throw new Error('Container-release builder lease is invalid JSON.');
768
+ }
769
+ const keys = Object.keys(lease ?? {}).sort();
770
+ if (
771
+ JSON.stringify(keys) !==
772
+ JSON.stringify([
773
+ 'builder',
774
+ 'cleanup',
775
+ 'createdAt',
776
+ 'driver',
777
+ 'kind',
778
+ 'ownerPid',
779
+ 'schemaVersion',
780
+ ]) ||
781
+ lease.kind !== 'ebk-container-release-builder-lease' ||
782
+ lease.schemaVersion !== 1 ||
783
+ lease.builder !== expectedName ||
784
+ !/^ebk-release-[a-f0-9]{32}$/.test(lease.builder) ||
785
+ lease.driver !== 'docker-container' ||
786
+ JSON.stringify(lease.cleanup) !==
787
+ JSON.stringify(['docker', 'buildx', 'rm', lease.builder]) ||
788
+ !Number.isSafeInteger(lease.ownerPid) ||
789
+ lease.ownerPid <= 0 ||
790
+ !Number.isFinite(Date.parse(lease.createdAt)) ||
791
+ new Date(lease.createdAt).toISOString() !== lease.createdAt
792
+ ) {
793
+ throw new Error('Container-release builder lease has an invalid identity.');
794
+ }
795
+ return lease;
796
+ };
797
+ const removeReleaseBuilderSignalHandlers = () => {
798
+ for (const [signal, handler] of releaseBuilderSignalHandlers) {
799
+ process.removeListener(signal, handler);
800
+ }
801
+ releaseBuilderSignalHandlers.clear();
802
+ };
803
+ const ensureReleaseBuilderSignalHandlers = () => {
804
+ if (releaseBuilderSignalHandlers.size > 0) return;
805
+ for (const signal of ['SIGINT', 'SIGTERM']) {
806
+ const handler = () => void handleReleaseBuilderSignal(signal);
807
+ releaseBuilderSignalHandlers.set(signal, handler);
808
+ process.on(signal, handler);
809
+ }
810
+ };
811
+ const removeReleaseBuilderSignalHandlersWhenIdle = () => {
812
+ if (!releaseBuilder && releaseToolTemporaryDirectories.size === 0) {
813
+ removeReleaseBuilderSignalHandlers();
814
+ }
815
+ };
816
+ const createReleaseToolTemporaryDirectory = (prefix) => {
817
+ const created = mkdtempSync(join(tmpdir(), prefix));
818
+ const metadata = lstatSync(created);
819
+ if (
820
+ !metadata.isDirectory() ||
821
+ metadata.isSymbolicLink() ||
822
+ !isAbsolute(created)
823
+ ) {
824
+ throw new Error('Container-release tool temporary directory is not exact.');
825
+ }
826
+ const temporary = realpathSync(created);
827
+ const canonicalMetadata = lstatSync(temporary);
828
+ if (
829
+ !isAbsolute(temporary) ||
830
+ !canonicalMetadata.isDirectory() ||
831
+ canonicalMetadata.isSymbolicLink() ||
832
+ canonicalMetadata.dev !== metadata.dev ||
833
+ canonicalMetadata.ino !== metadata.ino
834
+ ) {
835
+ throw new Error('Container-release tool temporary directory is not canonical.');
836
+ }
837
+ releaseToolTemporaryDirectories.add(temporary);
838
+ ensureReleaseBuilderSignalHandlers();
839
+ return temporary;
840
+ };
841
+ const removeExactReleaseToolTemporaryDirectory = (temporary) => {
842
+ if (!releaseToolTemporaryDirectories.has(temporary)) {
843
+ throw new Error('Container-release tool temporary directory is not owned.');
844
+ }
845
+ const metadata = lstatSync(temporary);
846
+ if (
847
+ !metadata.isDirectory() ||
848
+ metadata.isSymbolicLink() ||
849
+ !isAbsolute(temporary) ||
850
+ realpathSync(temporary) !== temporary
851
+ ) {
852
+ throw new Error(
853
+ 'Container-release tool temporary directory changed identity before cleanup.',
854
+ );
855
+ }
856
+ rmSync(temporary, { force: true, recursive: true });
857
+ if (existsSync(temporary)) {
858
+ throw new Error('Container-release tool temporary directory cleanup failed.');
859
+ }
860
+ releaseToolTemporaryDirectories.delete(temporary);
861
+ removeReleaseBuilderSignalHandlersWhenIdle();
862
+ };
863
+ const removeAllReleaseToolTemporaryDirectories = () => {
864
+ const cleanupErrors = [];
865
+ for (const temporary of [...releaseToolTemporaryDirectories]) {
866
+ try {
867
+ removeExactReleaseToolTemporaryDirectory(temporary);
868
+ } catch (error) {
869
+ cleanupErrors.push(error);
870
+ }
871
+ }
872
+ if (cleanupErrors.length > 0) {
873
+ throw new AggregateError(
874
+ cleanupErrors,
875
+ 'Exact container-release tool temporary cleanup failed.',
876
+ );
877
+ }
878
+ };
879
+ const releaseBuilderCleanupDiagnostic = (builder) =>
880
+ 'docker buildx rm ' + builder.name +
881
+ ' (ownership lease: ' + builder.leasePath + ')';
882
+ const listExactReleaseBuilderNames = ({ signal } = {}) => {
883
+ const inventory = spawnSync(
884
+ 'docker',
885
+ ['buildx', 'ls', '--format', '{{json .Name}}'],
886
+ {
887
+ cwd: repositoryRoot,
888
+ encoding: 'utf8',
889
+ env: process.env,
890
+ maxBuffer: 1024 * 1024,
891
+ stdio: ['ignore', 'pipe', 'pipe'],
892
+ timeout: signal ? 5_000 : undefined,
893
+ },
894
+ );
895
+ if (inventory.error || inventory.signal || inventory.status !== 0) {
896
+ throw new Error(
897
+ 'Invocation-owned Buildx inventory failed while proving exact cleanup: ' +
898
+ String(
899
+ inventory.stderr ||
900
+ inventory.error?.message ||
901
+ inventory.signal ||
902
+ inventory.status,
903
+ ).slice(-2_000),
904
+ );
905
+ }
906
+ const lines = inventory.stdout.trim() === ''
907
+ ? []
908
+ : inventory.stdout.trim().split(/\\r?\\n/u);
909
+ if (lines.length > 256) {
910
+ throw new Error('Invocation-owned Buildx inventory is unbounded.');
911
+ }
912
+ const names = new Set();
913
+ for (const line of lines) {
914
+ let name;
915
+ try {
916
+ name = JSON.parse(line);
917
+ } catch {
918
+ throw new Error('Invocation-owned Buildx inventory is not exact JSON.');
919
+ }
920
+ if (
921
+ typeof name !== 'string' ||
922
+ name.length === 0 ||
923
+ name.length > 256 ||
924
+ names.has(name)
925
+ ) {
926
+ throw new Error('Invocation-owned Buildx inventory has an invalid identity.');
927
+ }
928
+ names.add(name);
929
+ }
930
+ return names;
931
+ };
932
+ const removeExactReleaseBuilder = (builder, { signal } = {}) => {
933
+ readReleaseBuilderLease(builder.leasePath, builder.name);
934
+ const cleanup = spawnSync(
935
+ 'docker',
936
+ ['buildx', 'rm', builder.name],
937
+ {
938
+ cwd: repositoryRoot,
939
+ encoding: 'utf8',
940
+ env: process.env,
941
+ maxBuffer: 1024 * 1024,
942
+ stdio: ['ignore', 'pipe', 'pipe'],
943
+ timeout: signal ? 5_000 : undefined,
944
+ },
945
+ );
946
+ if (cleanup.error || cleanup.signal || cleanup.status !== 0) {
947
+ let builderNames;
948
+ try {
949
+ builderNames = listExactReleaseBuilderNames({ signal });
950
+ } catch (inventoryError) {
951
+ const error = new AggregateError(
952
+ [
953
+ cleanup.error ?? new Error(String(cleanup.stderr || cleanup.status)),
954
+ inventoryError,
955
+ ],
956
+ 'Invocation-owned Buildx cleanup and exact absence proof both failed; ' +
957
+ 'retain ' + releaseBuilderCleanupDiagnostic(builder) + '.',
958
+ );
959
+ if (signal) {
960
+ process.stderr.write(error.message + '\\n');
961
+ return false;
962
+ }
963
+ throw error;
964
+ }
965
+ if (!builderNames.has(builder.name)) {
966
+ unlinkSync(builder.leasePath);
967
+ process.stderr.write(
968
+ 'ebk-release-builder-already-absent ' +
969
+ JSON.stringify({ builder: builder.name, leasePath: builder.leasePath }) +
970
+ '\\n',
971
+ );
972
+ return true;
973
+ }
974
+ const diagnostic = [cleanup.stdout, cleanup.stderr]
975
+ .filter(Boolean)
976
+ .join('\\n')
977
+ .slice(-2_000);
978
+ const error = new Error(
979
+ 'Invocation-owned Buildx cleanup failed; use only the exact retained ' +
980
+ 'hard-kill cleanup command ' + releaseBuilderCleanupDiagnostic(builder) +
981
+ (diagnostic ? ': ' + diagnostic : ''),
982
+ );
983
+ if (signal) {
984
+ process.stderr.write(error.message + '\\n');
985
+ return false;
986
+ }
987
+ throw error;
988
+ }
989
+ unlinkSync(builder.leasePath);
990
+ return true;
991
+ };
992
+ const recoverReleaseBuilders = () => {
993
+ const leaseRoot = releaseBuilderLeaseRoot();
994
+ const entries = readdirSync(leaseRoot, { withFileTypes: true }).sort(
995
+ (left, right) =>
996
+ left.name < right.name ? -1 : left.name > right.name ? 1 : 0,
997
+ );
998
+ if (entries.length > 64) {
999
+ throw new Error('Container-release builder lease inventory is unbounded.');
1000
+ }
1001
+ for (const entry of entries) {
1002
+ const matched = /^(ebk-release-[a-f0-9]{32})\\.json$/.exec(entry.name);
1003
+ if (!entry.isFile() || !matched) {
1004
+ throw new Error(
1005
+ 'Container-release builder lease directory contains an unexpected entry.',
1006
+ );
1007
+ }
1008
+ const leasePath = join(leaseRoot, entry.name);
1009
+ const lease = readReleaseBuilderLease(leasePath, matched[1]);
1010
+ const builderNames = listExactReleaseBuilderNames();
1011
+ if (builderNames.has(lease.builder)) {
1012
+ throw new Error(
1013
+ 'A retained invocation-owned Buildx builder still exists; inspect it and use only the exact cleanup command ' +
1014
+ releaseBuilderCleanupDiagnostic({ leasePath, name: lease.builder }) + '.',
1015
+ );
1016
+ }
1017
+ unlinkSync(leasePath);
1018
+ process.stderr.write(
1019
+ 'ebk-release-builder-recovered ' +
1020
+ JSON.stringify({ builder: lease.builder, leasePath }) +
1021
+ '\\n',
1022
+ );
1023
+ }
1024
+ };
1025
+ const cleanupReleaseBuilder = ({ signal } = {}) => {
1026
+ const builder = releaseBuilder;
1027
+ if (!builder) {
1028
+ removeReleaseBuilderSignalHandlersWhenIdle();
1029
+ return;
1030
+ }
1031
+ releaseBuilder = undefined;
1032
+ const removed = removeExactReleaseBuilder(builder, { signal });
1033
+ removeReleaseBuilderSignalHandlersWhenIdle();
1034
+ return removed;
1035
+ };
1036
+ const handleReleaseBuilderSignal = async (signal) => {
1037
+ if (releaseBuilderSignal) return;
1038
+ releaseBuilderSignal = signal;
1039
+ removeReleaseBuilderSignalHandlers();
1040
+ const cleanupErrors = [];
1041
+ let childStopped = false;
1042
+ try {
1043
+ await stopExactReleaseBuildChild(signal);
1044
+ childStopped = true;
1045
+ } catch (error) {
1046
+ cleanupErrors.push(error);
1047
+ }
1048
+ if (childStopped) {
1049
+ try {
1050
+ cleanupReleaseBuilder({ signal });
1051
+ } catch (error) {
1052
+ cleanupErrors.push(error);
1053
+ }
1054
+ }
1055
+ try {
1056
+ removeAllReleaseToolTemporaryDirectories();
1057
+ } catch (error) {
1058
+ cleanupErrors.push(error);
1059
+ }
1060
+ if (cleanupErrors.length > 0) {
1061
+ process.stderr.write(
1062
+ 'Invocation-owned Buildx signal cleanup failed closed; the exact ' +
1063
+ 'ownership lease was retained: ' +
1064
+ cleanupErrors
1065
+ .map((error) => String(error?.message ?? error))
1066
+ .join('; ')
1067
+ .slice(-2_000) + '\\n',
1068
+ );
1069
+ }
1070
+ try {
1071
+ removeReleaseBuilderSignalHandlers();
1072
+ } finally {
1073
+ process.kill(process.pid, signal);
1074
+ }
1075
+ };
1076
+ const ensureReleaseBuilder = () => {
1077
+ if (releaseBuilder) return releaseBuilder;
1078
+ const name = 'ebk-release-' + randomBytes(16).toString('hex');
1079
+ const leaseRoot = releaseBuilderLeaseRoot();
1080
+ const leasePath = join(leaseRoot, name + '.json');
1081
+ const builder = {
1082
+ leasePath,
1083
+ leaseRoot,
1084
+ name,
1085
+ };
1086
+ writeFileSync(
1087
+ leasePath,
1088
+ JSON.stringify({
1089
+ builder: name,
1090
+ cleanup: ['docker', 'buildx', 'rm', name],
1091
+ createdAt: new Date().toISOString(),
1092
+ driver: 'docker-container',
1093
+ kind: 'ebk-container-release-builder-lease',
1094
+ ownerPid: process.pid,
1095
+ schemaVersion: 1,
1096
+ }) + '\\n',
1097
+ { encoding: 'utf8', flag: 'wx', mode: 0o600 },
1098
+ );
1099
+ hardenPrivateReleaseBuilderPath(leasePath, 'file');
1100
+ releaseBuilder = builder;
1101
+ ensureReleaseBuilderSignalHandlers();
1102
+ process.stderr.write(
1103
+ 'ebk-release-builder-ownership ' +
1104
+ JSON.stringify({
1105
+ builder: name,
1106
+ hardKillCleanup: ['docker', 'buildx', 'rm', name],
1107
+ leasePath,
1108
+ }) +
1109
+ '\\n',
1110
+ );
1111
+ try {
1112
+ execute(
1113
+ 'docker',
1114
+ ['buildx', 'create', '--name', name, '--driver', 'docker-container'],
1115
+ 'Invocation-owned native Buildx builder creation',
1116
+ );
1117
+ } catch (error) {
1118
+ try {
1119
+ cleanupReleaseBuilder();
1120
+ } catch (cleanupError) {
1121
+ throw new AggregateError(
1122
+ [error, cleanupError],
1123
+ 'Invocation-owned Buildx creation and cleanup both failed.',
1124
+ );
1125
+ }
1126
+ throw error;
1127
+ }
1128
+ return builder;
1129
+ };
299
1130
  const awsJson = (args, label) => {
300
1131
  const raw = execute('aws', args, label);
301
1132
  try {
@@ -332,11 +1163,12 @@ const describeRepository = (repositoryName) => {
332
1163
  throw new Error('ECR repository lookup returned invalid JSON.');
333
1164
  }
334
1165
  }
335
- const error = String(result.stderr ?? '');
1166
+ const error = String(result.stderr ?? '').trimStart();
1167
+ const notFound =
1168
+ 'An error occurred (RepositoryNotFoundException) when calling the DescribeRepositories operation:';
336
1169
  if (
337
- !error.startsWith(
338
- 'An error occurred (RepositoryNotFoundException) when calling the DescribeRepositories operation:',
339
- )
1170
+ !error.startsWith(notFound) &&
1171
+ !error.startsWith('aws: [ERROR]: ' + notFound)
340
1172
  ) {
341
1173
  throw new Error('ECR repository lookup failed outside the create-if-absent path.');
342
1174
  }
@@ -349,6 +1181,18 @@ const caller = awsJson(
349
1181
  if (caller?.Account !== accountId) {
350
1182
  throw new Error('Container release AWS caller differs from the target account.');
351
1183
  }
1184
+ execute(
1185
+ process.execPath,
1186
+ [
1187
+ safePath(
1188
+ 'tools/stage-authority-render.mjs',
1189
+ 'Workspace ownership doctor',
1190
+ ),
1191
+ '--stage=' + stage,
1192
+ '--ownership-doctor=true',
1193
+ ],
1194
+ 'Workspace stage ownership',
1195
+ );
352
1196
  const controls = readJson(
353
1197
  safePath('operations/release.json', 'Release controls', { base: sliceRoot }),
354
1198
  'Release controls',
@@ -404,10 +1248,17 @@ const releaseContainerInputs = declaredContainers.map((declared, index) => {
404
1248
  ? captured.digest
405
1249
  : captured?.candidateDigest;
406
1250
  const candidate = declared.candidate;
407
- const materialDigest = digestCandidatePaths(
408
- candidate.materialPaths,
409
- 'OCI artifact candidate material',
410
- );
1251
+ // Promotion copies reviewed staging images without rebuilding, so the build
1252
+ // material is never present in that job; the candidate evidence restored
1253
+ // from the exact release artifact already binds the material digest
1254
+ // (EBK-INT-2026-076).
1255
+ const materialDigest =
1256
+ mode === 'promote'
1257
+ ? candidateArtifactDigest
1258
+ : digestCandidatePaths(
1259
+ candidate.materialPaths,
1260
+ 'OCI artifact candidate material',
1261
+ );
411
1262
  const buildRecipeDigest = digestCandidatePaths(
412
1263
  candidate.recipePaths,
413
1264
  'OCI artifact candidate recipe',
@@ -684,18 +1535,132 @@ const exactImageByTag = (repositoryName, tag) => {
684
1535
  }
685
1536
  return response.imageDetails[0].imageDigest;
686
1537
  };
1538
+ const exactImageByDigest = (repositoryName, imageDigest) => {
1539
+ const response = awsJson(
1540
+ [
1541
+ 'ecr',
1542
+ 'describe-images',
1543
+ '--registry-id',
1544
+ accountId,
1545
+ '--repository-name',
1546
+ repositoryName,
1547
+ '--image-ids',
1548
+ 'imageDigest=' + imageDigest,
1549
+ '--region',
1550
+ region,
1551
+ '--output',
1552
+ 'json',
1553
+ ],
1554
+ 'Exact ECR image digest lookup',
1555
+ );
1556
+ return (
1557
+ Array.isArray(response?.imageDetails) &&
1558
+ response.imageDetails.length === 1 &&
1559
+ response.imageDetails[0]?.registryId === accountId &&
1560
+ response.imageDetails[0]?.repositoryName === repositoryName &&
1561
+ response.imageDetails[0]?.imageDigest === imageDigest
1562
+ );
1563
+ };
1564
+ // An unchanged artifact (same candidate material, recipe, Dockerfile, platform,
1565
+ // and production closure) may reuse the image and SBOM retained by the newest
1566
+ // successful deployment on this branch instead of rebuilding. Declining reuse
1567
+ // is always safe: the full native build runs. The native run proof is always
1568
+ // re-executed against the reused image.
1569
+ const reusableBaselinePlatformRecord = (input, requestedPlatform, architecture, repositoryName, expectedClosure) => {
1570
+ const root = optionalCacheRoot('EBK_CONTAINER_BASELINE_ROOT');
1571
+ if (root === '' || process.env.EBK_OCI_REBUILD?.trim() === 'all') return undefined;
1572
+ const safeArtifact = input.declared.name.replace(/[^A-Za-z0-9._-]/g, '_');
1573
+ const recordPath = join(
1574
+ root,
1575
+ 'container-release',
1576
+ 'partials',
1577
+ stage,
1578
+ slice,
1579
+ architecture,
1580
+ safeArtifact + '.platform.json',
1581
+ );
1582
+ const decline = (reason) => {
1583
+ process.stderr.write(
1584
+ 'ebk-container-release-reuse ' +
1585
+ JSON.stringify({ artifact: input.declared.id, platform: requestedPlatform, reason, reused: false }) +
1586
+ '\\n',
1587
+ );
1588
+ return undefined;
1589
+ };
1590
+ if (!existsSync(recordPath) || lstatSync(recordPath).isSymbolicLink()) {
1591
+ return decline('no baseline platform record');
1592
+ }
1593
+ let record;
1594
+ try {
1595
+ record = validateContainerPlatformEvidence(JSON.parse(readFileSync(recordPath, 'utf8')));
1596
+ } catch (error) {
1597
+ return decline('baseline platform record is invalid: ' + String(error?.message ?? error));
1598
+ }
1599
+ if (
1600
+ record.accountId !== accountId ||
1601
+ record.region !== region ||
1602
+ record.stage !== stage ||
1603
+ record.slice !== slice ||
1604
+ record.artifactId !== input.declared.id ||
1605
+ record.artifactName !== input.declared.name ||
1606
+ record.platform !== requestedPlatform ||
1607
+ record.repositoryName !== repositoryName ||
1608
+ record.candidateArtifactDigest !== input.candidateArtifactDigest ||
1609
+ record.dockerfileDigest !== digestBytes(readFileSync(input.dockerfile)) ||
1610
+ record.productionClosureDigest !== expectedClosure.digest ||
1611
+ !IMAGE_DIGEST.test(record.imageDigest) ||
1612
+ record.imageReference !== registry + '/' + repositoryName + '@' + record.imageDigest
1613
+ ) {
1614
+ return decline('artifact inputs changed since the baseline');
1615
+ }
1616
+ const evidencePrefix = '.ebk/evidence/';
1617
+ const sbomPath = join(
1618
+ root,
1619
+ record.sbom.path.startsWith(evidencePrefix)
1620
+ ? record.sbom.path.slice(evidencePrefix.length)
1621
+ : record.sbom.path,
1622
+ );
1623
+ if (!existsSync(sbomPath) || lstatSync(sbomPath).isSymbolicLink()) {
1624
+ return decline('baseline SBOM is absent');
1625
+ }
1626
+ const sbomBytes = readFileSync(sbomPath);
1627
+ if (digestBytes(sbomBytes) !== record.sbom.sha256) {
1628
+ return decline('baseline SBOM bytes differ from the retained record');
1629
+ }
1630
+ if (!exactImageByDigest(repositoryName, record.imageDigest)) {
1631
+ return decline('baseline image is no longer in the registry');
1632
+ }
1633
+ process.stderr.write(
1634
+ 'ebk-container-release-reuse ' +
1635
+ JSON.stringify({
1636
+ artifact: input.declared.id,
1637
+ baselineCommit: record.sourceCommit,
1638
+ imageDigest: record.imageDigest,
1639
+ platform: requestedPlatform,
1640
+ reused: true,
1641
+ }) +
1642
+ '\\n',
1643
+ );
1644
+ return { imageDigest: record.imageDigest, sbomBytes };
1645
+ };
687
1646
  const acquireSyft = async () => {
688
1647
  const platformKey = process.platform + '-' + process.arch;
689
1648
  const release = SYFT_RELEASES[platformKey];
690
1649
  if (!release) {
691
1650
  throw new Error('No reviewed Syft release exists for ' + platformKey + '.');
692
1651
  }
693
- const temporary = mkdtempSync(join(tmpdir(), 'ebk-container-syft-'));
1652
+ const temporary = createReleaseToolTemporaryDirectory('ebk-container-syft-');
1653
+ try {
694
1654
  const supplied = process.env.EBK_SYFT_ARCHIVE_PATH?.trim();
695
1655
  const archive = supplied
696
1656
  ? safePath(relative(repositoryRoot, resolve(repositoryRoot, supplied)), 'Supplied Syft archive')
697
1657
  : join(temporary, 'syft-archive');
698
- if (!supplied) {
1658
+ const cachedArchive = supplied ? '' : cachedReleaseToolArchive('syft', release.sha256);
1659
+ if (!supplied && cachedArchive && existsSync(cachedArchive)) {
1660
+ // Reuse the archive verified by an earlier invocation on this runner; the
1661
+ // digest check below still runs against the copied bytes.
1662
+ copyFileSync(cachedArchive, archive);
1663
+ } else if (!supplied) {
699
1664
  const response = await fetch(release.source, {
700
1665
  redirect: 'follow',
701
1666
  signal: AbortSignal.timeout(120_000),
@@ -720,6 +1685,10 @@ const acquireSyft = async () => {
720
1685
  ) {
721
1686
  throw new Error('Pinned Syft archive integrity does not match policy.');
722
1687
  }
1688
+ if (cachedArchive && !existsSync(cachedArchive)) {
1689
+ mkdirSync(optionalCacheRoot('EBK_RELEASE_TOOL_CACHE_DIR'), { mode: 0o700, recursive: true });
1690
+ writeFileSync(cachedArchive, bytes, { mode: 0o600 });
1691
+ }
723
1692
  const path = join(temporary, 'syft');
724
1693
  if (release.archiveFormat === 'binary') {
725
1694
  writeFileSync(path, bytes, { mode: 0o700 });
@@ -728,6 +1697,7 @@ const acquireSyft = async () => {
728
1697
  chmodSync(path, 0o700);
729
1698
  }
730
1699
  const version = String(execute(path, ['version'], 'Syft version'));
1700
+ await yieldToReleaseSignals();
731
1701
  if (
732
1702
  !new RegExp('(?:Version|version):?\\\\s*' + SYFT_VERSION.replaceAll('.', '\\\\.')).test(
733
1703
  version,
@@ -735,7 +1705,18 @@ const acquireSyft = async () => {
735
1705
  ) {
736
1706
  throw new Error('Syft runtime version differs from reviewed policy.');
737
1707
  }
738
- return { path, temporary };
1708
+ return { path, temporary };
1709
+ } catch (error) {
1710
+ try {
1711
+ removeExactReleaseToolTemporaryDirectory(temporary);
1712
+ } catch (cleanupError) {
1713
+ throw new AggregateError(
1714
+ [error, cleanupError],
1715
+ 'Syft acquisition and exact temporary cleanup both failed.',
1716
+ );
1717
+ }
1718
+ throw error;
1719
+ }
739
1720
  };
740
1721
  const acquireCrane = async () => {
741
1722
  const platformKey = process.platform + '-' + process.arch;
@@ -743,7 +1724,8 @@ const acquireCrane = async () => {
743
1724
  if (!release) {
744
1725
  throw new Error('No reviewed Crane release exists for ' + platformKey + '.');
745
1726
  }
746
- const temporary = mkdtempSync(join(tmpdir(), 'ebk-container-crane-'));
1727
+ const temporary = createReleaseToolTemporaryDirectory('ebk-container-crane-');
1728
+ try {
747
1729
  const supplied = process.env.EBK_CRANE_ARCHIVE_PATH?.trim();
748
1730
  const archive = supplied
749
1731
  ? safePath(
@@ -751,7 +1733,10 @@ const acquireCrane = async () => {
751
1733
  'Supplied Crane archive',
752
1734
  )
753
1735
  : join(temporary, 'crane-archive.tar.gz');
754
- if (!supplied) {
1736
+ const cachedArchive = supplied ? '' : cachedReleaseToolArchive('crane', release.sha256);
1737
+ if (!supplied && cachedArchive && existsSync(cachedArchive)) {
1738
+ copyFileSync(cachedArchive, archive);
1739
+ } else if (!supplied) {
755
1740
  const response = await fetch(release.source, {
756
1741
  redirect: 'follow',
757
1742
  signal: AbortSignal.timeout(120_000),
@@ -776,10 +1761,15 @@ const acquireCrane = async () => {
776
1761
  ) {
777
1762
  throw new Error('Pinned Crane archive integrity does not match policy.');
778
1763
  }
1764
+ if (cachedArchive && !existsSync(cachedArchive)) {
1765
+ mkdirSync(optionalCacheRoot('EBK_RELEASE_TOOL_CACHE_DIR'), { mode: 0o700, recursive: true });
1766
+ writeFileSync(cachedArchive, bytes, { mode: 0o600 });
1767
+ }
779
1768
  execute('tar', ['-xzf', archive, '-C', temporary, 'crane'], 'Crane extraction');
780
1769
  const path = join(temporary, 'crane');
781
1770
  chmodSync(path, 0o700);
782
1771
  const version = String(execute(path, ['version'], 'Crane version')).trim();
1772
+ await yieldToReleaseSignals();
783
1773
  if (
784
1774
  !new RegExp('(?:^|\\s|v)' + CRANE_VERSION.replaceAll('.', '\\.') + '(?:$|\\s)').test(
785
1775
  version,
@@ -787,12 +1777,23 @@ const acquireCrane = async () => {
787
1777
  ) {
788
1778
  throw new Error('Crane runtime version differs from reviewed policy.');
789
1779
  }
790
- return { path, temporary };
1780
+ return { path, temporary };
1781
+ } catch (error) {
1782
+ try {
1783
+ removeExactReleaseToolTemporaryDirectory(temporary);
1784
+ } catch (cleanupError) {
1785
+ throw new AggregateError(
1786
+ [error, cleanupError],
1787
+ 'Crane acquisition and exact temporary cleanup both failed.',
1788
+ );
1789
+ }
1790
+ throw error;
1791
+ }
791
1792
  };
792
1793
  const parseCycloneDx = (value, label) => {
793
1794
  if (
794
1795
  value?.bomFormat !== 'CycloneDX' ||
795
- value.specVersion !== '1.6' ||
1796
+ value.specVersion !== '1.7' ||
796
1797
  value.version !== 1 ||
797
1798
  !/^urn:uuid:[a-fA-F0-9-]{36}$/.test(value.serialNumber ?? '') ||
798
1799
  !Array.isArray(value.components) ||
@@ -801,17 +1802,30 @@ const parseCycloneDx = (value, label) => {
801
1802
  !value.metadata ||
802
1803
  typeof value.metadata !== 'object'
803
1804
  ) {
804
- throw new Error(label + ' is not complete CycloneDX 1.6.');
1805
+ throw new Error(label + ' is not complete CycloneDX 1.7.');
805
1806
  }
806
1807
  return value;
807
1808
  };
1809
+ const isVersionlessSyftFileComponent = (component) =>
1810
+ component?.type === 'file' &&
1811
+ component.version === undefined &&
1812
+ component.purl === undefined &&
1813
+ typeof component.name === 'string' &&
1814
+ component.name.startsWith('/') &&
1815
+ component.name.length <= 4_096 &&
1816
+ Array.isArray(component.hashes) &&
1817
+ component.hashes.length === 2 &&
1818
+ component.hashes[0]?.alg === 'SHA-1' &&
1819
+ /^[a-f0-9]{40}$/.test(component.hashes[0]?.content ?? '') &&
1820
+ component.hashes[1]?.alg === 'SHA-256' &&
1821
+ /^[a-f0-9]{64}$/.test(component.hashes[1]?.content ?? '');
808
1822
  const componentSetFor = (sbom) =>
809
1823
  sbom.components
810
1824
  .map((component) => ({
811
1825
  name: component?.name,
812
1826
  purl: component?.purl ?? '',
813
1827
  type: component?.type,
814
- version: component?.version,
1828
+ version: component?.version ?? '',
815
1829
  }))
816
1830
  .sort((left, right) =>
817
1831
  JSON.stringify(left) < JSON.stringify(right)
@@ -874,15 +1888,19 @@ const npmClosureFor = (syftDocument, options = {}) => {
874
1888
  }
875
1889
  return unique;
876
1890
  };
877
- const deriveExpectedProductionClosure = (syft, input) => {
878
- const temporary = mkdtempSync(join(tmpdir(), 'ebk-container-closure-'));
1891
+ const deriveExpectedProductionClosure = async (syft, input) => {
1892
+ const temporary = createReleaseToolTemporaryDirectory('ebk-container-closure-');
879
1893
  const deployed = join(temporary, 'deployed');
880
1894
  try {
881
1895
  execute(
882
1896
  'pnpm',
883
1897
  [
1898
+ '--config.store-dir=' +
1899
+ safePath('.ebk/cache/pnpm-store', 'Workspace pnpm store'),
1900
+ '--config.inject-workspace-packages=true',
884
1901
  '--filter',
885
1902
  input.packageName,
1903
+ '--trust-lockfile',
886
1904
  'deploy',
887
1905
  '--prod',
888
1906
  '--offline',
@@ -890,38 +1908,79 @@ const deriveExpectedProductionClosure = (syft, input) => {
890
1908
  deployed,
891
1909
  ],
892
1910
  'Independent production closure deployment',
893
- { env: { COREPACK_ENABLE_DOWNLOAD_PROMPT: '0' } },
1911
+ {
1912
+ env: { COREPACK_ENABLE_DOWNLOAD_PROMPT: '0' },
1913
+ failureDiagnostics: 'bounded-closure',
1914
+ },
894
1915
  );
1916
+ await yieldToReleaseSignals();
895
1917
  const raw = String(
896
1918
  execute(
897
1919
  syft.path,
898
1920
  ['scan', 'dir:' + deployed, '--output', 'syft-json'],
899
1921
  'Independent production closure inventory',
900
- { env: { SYFT_CHECK_FOR_APP_UPDATE: 'false' } },
1922
+ {
1923
+ env: { SYFT_CHECK_FOR_APP_UPDATE: 'false' },
1924
+ failureDiagnostics: 'bounded-closure',
1925
+ },
901
1926
  ),
902
1927
  );
1928
+ await yieldToReleaseSignals();
903
1929
  let inventory;
904
1930
  try {
905
1931
  inventory = JSON.parse(raw);
906
1932
  } catch {
907
1933
  throw new Error('Independent production closure inventory is invalid JSON.');
908
1934
  }
1935
+ const sourceManifest = readJson(
1936
+ input.packageManifestPath,
1937
+ 'Container package manifest',
1938
+ );
1939
+ const deployedManifest = readJson(
1940
+ join(deployed, 'package.json'),
1941
+ 'Deployed container package manifest',
1942
+ );
1943
+ if (
1944
+ sourceManifest.name !== input.packageName ||
1945
+ typeof sourceManifest.version !== 'string' ||
1946
+ sourceManifest.version.length === 0 ||
1947
+ sourceManifest.version.length > 128 ||
1948
+ deployedManifest.name !== sourceManifest.name ||
1949
+ deployedManifest.version !== sourceManifest.version
1950
+ ) {
1951
+ throw new Error(
1952
+ 'Independent production closure changed the workload package identity.',
1953
+ );
1954
+ }
909
1955
  const packages = npmClosureFor(inventory);
910
1956
  if (
911
- !packages.some(
1957
+ packages.some(
912
1958
  ({ name, version }) =>
913
- name === input.packageName &&
914
- version === readJson(
915
- input.packageManifestPath,
916
- 'Container package manifest',
917
- ).version,
1959
+ name === sourceManifest.name && version !== sourceManifest.version,
918
1960
  )
919
1961
  ) {
920
- throw new Error('Independent production closure omits the workload package.');
1962
+ throw new Error(
1963
+ 'Independent production closure contains a conflicting workload package identity.',
1964
+ );
1965
+ }
1966
+ if (!packages.some(({ name }) => name === sourceManifest.name)) {
1967
+ packages.push({
1968
+ name: sourceManifest.name,
1969
+ version: sourceManifest.version,
1970
+ });
1971
+ packages.sort(
1972
+ (left, right) =>
1973
+ (left.name < right.name ? -1 : left.name > right.name ? 1 : 0) ||
1974
+ (left.version < right.version
1975
+ ? -1
1976
+ : left.version > right.version
1977
+ ? 1
1978
+ : 0),
1979
+ );
921
1980
  }
922
1981
  return { digest: supplyChainDigest(packages), packages };
923
1982
  } finally {
924
- rmSync(temporary, { force: true, recursive: true });
1983
+ removeExactReleaseToolTemporaryDirectory(temporary);
925
1984
  }
926
1985
  };
927
1986
  const validateRetainedSbom = (entry, label) => {
@@ -1210,13 +2269,26 @@ if (stage === 'development') {
1210
2269
  process.env.EBK_STAGING_PROMOTION_EXPECTED_ACCOUNT_ID?.trim() ?? '';
1211
2270
  const expectedSourceRegion =
1212
2271
  process.env.EBK_STAGING_PROMOTION_REGION?.trim() ?? '';
2272
+ const deploymentProfile = process.env.EBK_DEPLOYMENT_PROFILE?.trim() ?? '';
2273
+ if (
2274
+ deploymentProfile !== 'starter' &&
2275
+ deploymentProfile !== 'production-isolated' &&
2276
+ deploymentProfile !== 'fully-isolated'
2277
+ ) {
2278
+ throw new Error('Container promotion requires one exact deployment profile.');
2279
+ }
2280
+ const sameAccountPromotion = deploymentProfile === 'starter';
1213
2281
  if (
1214
2282
  !/^\\d{12}$/.test(expectedSourceAccountId) ||
1215
2283
  !AWS_REGION_NAME.test(expectedSourceRegion) ||
1216
- expectedSourceAccountId === accountId
2284
+ (sameAccountPromotion
2285
+ ? expectedSourceAccountId !== accountId || expectedSourceRegion !== region
2286
+ : expectedSourceAccountId === accountId)
1217
2287
  ) {
1218
2288
  throw new Error(
1219
- 'Development retirement promotion requires one independent staging account and Region.',
2289
+ sameAccountPromotion
2290
+ ? 'Starter promotion requires the exact shared account and Region.'
2291
+ : 'Isolated promotion requires one independent staging account and exact Region.',
1220
2292
  );
1221
2293
  }
1222
2294
  const sourceBindingPath = safePath(
@@ -1310,13 +2382,26 @@ if (mode === 'promote') {
1310
2382
  process.env.EBK_STAGING_PROMOTION_EXPECTED_ACCOUNT_ID?.trim() ?? '';
1311
2383
  const expectedSourceRegion =
1312
2384
  process.env.EBK_STAGING_PROMOTION_REGION?.trim() ?? '';
2385
+ const deploymentProfile = process.env.EBK_DEPLOYMENT_PROFILE?.trim() ?? '';
2386
+ if (
2387
+ deploymentProfile !== 'starter' &&
2388
+ deploymentProfile !== 'production-isolated' &&
2389
+ deploymentProfile !== 'fully-isolated'
2390
+ ) {
2391
+ throw new Error('OCI promotion requires one exact deployment profile.');
2392
+ }
2393
+ const sameAccountPromotion = deploymentProfile === 'starter';
1313
2394
  if (
1314
2395
  !/^\\d{12}$/.test(expectedSourceAccountId) ||
1315
2396
  !AWS_REGION_NAME.test(expectedSourceRegion) ||
1316
- expectedSourceAccountId === accountId
2397
+ (sameAccountPromotion
2398
+ ? expectedSourceAccountId !== accountId || expectedSourceRegion !== region
2399
+ : expectedSourceAccountId === accountId)
1317
2400
  ) {
1318
2401
  throw new Error(
1319
- 'OCI promotion requires one bounded independent staging account and Region.',
2402
+ sameAccountPromotion
2403
+ ? 'Starter OCI promotion requires the exact shared account and Region.'
2404
+ : 'Isolated OCI promotion requires one bounded independent staging account and exact Region.',
1320
2405
  );
1321
2406
  }
1322
2407
  const sourceBindingInput =
@@ -1573,6 +2658,7 @@ if (mode === 'promote') {
1573
2658
  slice,
1574
2659
  'binding.json',
1575
2660
  );
2661
+ await yieldToReleaseSignals();
1576
2662
  immutableWrite(output, binding);
1577
2663
  process.stdout.write(
1578
2664
  JSON.stringify({
@@ -1585,30 +2671,41 @@ if (mode === 'promote') {
1585
2671
  }) + '\\n',
1586
2672
  );
1587
2673
  } finally {
1588
- rmSync(crane.temporary, { force: true, recursive: true });
2674
+ removeExactReleaseToolTemporaryDirectory(crane.temporary);
1589
2675
  }
1590
2676
  process.exit(0);
1591
2677
  }
1592
2678
 
1593
2679
  if (mode === 'platform') {
1594
- const nativeArchitecture = process.arch === 'x64' ? 'amd64' : process.arch;
1595
- if (
1596
- process.platform !== 'linux' ||
1597
- requestedPlatform !== 'linux/' + nativeArchitecture
1598
- ) {
1599
- throw new Error(
1600
- 'Platform evidence must run on the matching native Linux architecture.',
1601
- );
1602
- }
1603
- const syft = await acquireSyft();
1604
- const crane = stage === 'development' ? await acquireCrane() : undefined;
2680
+ let syft;
2681
+ let crane;
2682
+ let platformError;
1605
2683
  try {
1606
- const expectedClosures = new Map(
1607
- containerInputs.map((input) => [
1608
- input.declared.id,
1609
- deriveExpectedProductionClosure(syft, input),
1610
- ]),
1611
- );
2684
+ syft = await acquireSyft();
2685
+ crane =
2686
+ stage === 'development' || optionalCacheRoot('EBK_CONTAINER_BASELINE_ROOT') !== ''
2687
+ ? await acquireCrane()
2688
+ : undefined;
2689
+ recoverReleaseBuilders();
2690
+ // Artifacts that share a package, recipe, and Dockerfile (the migration and
2691
+ // retirement jobs) resolve one identical production closure.
2692
+ const expectedClosures = new Map();
2693
+ const closuresByRecipe = new Map();
2694
+ for (const input of containerInputs) {
2695
+ const recipeKey =
2696
+ input.packageName +
2697
+ ':' +
2698
+ input.buildRecipeDigest +
2699
+ ':' +
2700
+ digestBytes(readFileSync(input.dockerfile));
2701
+ if (!closuresByRecipe.has(recipeKey)) {
2702
+ closuresByRecipe.set(
2703
+ recipeKey,
2704
+ await deriveExpectedProductionClosure(syft, input),
2705
+ );
2706
+ }
2707
+ expectedClosures.set(input.declared.id, closuresByRecipe.get(recipeKey));
2708
+ }
1612
2709
  requireReviewedCheckout();
1613
2710
  for (const input of containerInputs) requireCandidateAuthority(input);
1614
2711
  login();
@@ -1639,7 +2736,24 @@ if (mode === 'platform') {
1639
2736
  ({ platform }) => platform === requestedPlatform,
1640
2737
  )
1641
2738
  : undefined;
1642
- if (stage === 'development') {
2739
+ const reusable =
2740
+ stage === 'development'
2741
+ ? undefined
2742
+ : reusableBaselinePlatformRecord(
2743
+ input,
2744
+ requestedPlatform,
2745
+ architecture,
2746
+ repositoryName,
2747
+ expectedClosure,
2748
+ );
2749
+ if (reusable) {
2750
+ if (!crane) throw new Error('Unchanged artifact reuse requires the reviewed crane.');
2751
+ execute(
2752
+ crane.path,
2753
+ ['copy', registry + '/' + repositoryName + '@' + reusable.imageDigest, taggedReference],
2754
+ 'Unchanged artifact re-tag without rebuilding',
2755
+ );
2756
+ } else if (stage === 'development') {
1643
2757
  if (!crane || !developmentSourcePlatform) {
1644
2758
  throw new Error(
1645
2759
  'Development retirement source platform is absent.',
@@ -1651,32 +2765,46 @@ if (mode === 'platform') {
1651
2765
  'Exact staging retirement child promotion',
1652
2766
  );
1653
2767
  } else {
1654
- execute(
1655
- 'docker',
1656
- [
1657
- 'buildx',
1658
- 'build',
1659
- '--file',
1660
- relative(repositoryRoot, input.dockerfile),
1661
- '--label',
1662
- 'org.opencontainers.image.revision=' + sourceCommit,
1663
- '--label',
1664
- 'dev.empire-builder-kit.candidate-digest=' +
1665
- input.candidateArtifactDigest,
1666
- '--platform',
1667
- requestedPlatform,
1668
- '--provenance=false',
1669
- '--push',
1670
- '--sbom=false',
1671
- '--tag',
1672
- taggedReference,
1673
- relative(repositoryRoot, input.context) || '.',
1674
- ],
1675
- 'Native container build and push',
1676
- );
2768
+ // One BuildKit builder serves every artifact of this invocation so
2769
+ // shared layers (pnpm fetch, filtered install) are reused across the
2770
+ // Slice's images; the platform-mode finally block removes it.
2771
+ const platformBuilder = ensureReleaseBuilder();
2772
+ const layerCache = buildxLayerCache(repositoryName, architecture);
2773
+ await executeObservedBuildx([
2774
+ 'buildx',
2775
+ 'build',
2776
+ '--builder',
2777
+ platformBuilder.name,
2778
+ ...(packageRegistry
2779
+ ? [
2780
+ '--build-arg',
2781
+ 'NPM_CONFIG_REGISTRY=' + packageRegistry,
2782
+ ]
2783
+ : []),
2784
+ ...layerCache.arguments,
2785
+ '--file',
2786
+ relative(repositoryRoot, input.dockerfile),
2787
+ '--label',
2788
+ 'org.opencontainers.image.revision=' + sourceCommit,
2789
+ '--label',
2790
+ 'dev.empire-builder-kit.candidate-digest=' +
2791
+ input.candidateArtifactDigest,
2792
+ '--platform',
2793
+ requestedPlatform,
2794
+ '--provenance=false',
2795
+ '--push',
2796
+ '--sbom=false',
2797
+ '--tag',
2798
+ taggedReference,
2799
+ relative(repositoryRoot, input.context) || '.',
2800
+ ], 'Native container build and push');
2801
+ layerCache.commit();
1677
2802
  }
1678
2803
  const imageDigest = exactImageByTag(repositoryName, tag);
1679
2804
  const imageReference = registry + '/' + repositoryName + '@' + imageDigest;
2805
+ if (reusable && reusable.imageDigest !== imageDigest) {
2806
+ throw new Error('Unchanged artifact re-tag differs from the baseline image.');
2807
+ }
1680
2808
  if (
1681
2809
  stage === 'development' &&
1682
2810
  developmentSourcePlatform?.imageDigest !== imageDigest
@@ -1752,39 +2880,43 @@ if (mode === 'platform') {
1752
2880
  resolve(repositoryRoot, relativeRunProofPath),
1753
2881
  serializedRunProof,
1754
2882
  );
1755
- const closureRaw = String(
1756
- execute(
1757
- syft.path,
1758
- ['scan', 'registry:' + imageReference, '--output', 'syft-json'],
1759
- 'Exact application npm closure inventory',
1760
- { env: { SYFT_CHECK_FOR_APP_UPDATE: 'false' } },
1761
- ),
1762
- );
1763
- let closureInventory;
1764
- try {
1765
- closureInventory = JSON.parse(closureRaw);
1766
- } catch {
1767
- throw new Error('Exact application npm closure inventory is invalid JSON.');
1768
- }
1769
- const observedClosure = npmClosureFor(closureInventory, {
1770
- applicationOnly: true,
1771
- });
1772
- if (
1773
- JSON.stringify(observedClosure) !==
1774
- JSON.stringify(expectedClosure.packages)
1775
- ) {
1776
- throw new Error(
1777
- 'Container application npm closure differs from the independently deployed production closure.',
2883
+ if (!reusable) {
2884
+ const closureRaw = String(
2885
+ execute(
2886
+ syft.path,
2887
+ ['scan', 'registry:' + imageReference, '--output', 'syft-json'],
2888
+ 'Exact application npm closure inventory',
2889
+ { env: { SYFT_CHECK_FOR_APP_UPDATE: 'false' } },
2890
+ ),
1778
2891
  );
2892
+ let closureInventory;
2893
+ try {
2894
+ closureInventory = JSON.parse(closureRaw);
2895
+ } catch {
2896
+ throw new Error('Exact application npm closure inventory is invalid JSON.');
2897
+ }
2898
+ const observedClosure = npmClosureFor(closureInventory, {
2899
+ applicationOnly: true,
2900
+ });
2901
+ if (
2902
+ JSON.stringify(observedClosure) !==
2903
+ JSON.stringify(expectedClosure.packages)
2904
+ ) {
2905
+ throw new Error(
2906
+ 'Container application npm closure differs from the independently deployed production closure.',
2907
+ );
2908
+ }
1779
2909
  }
1780
- const rawSbom = String(
1781
- execute(
1782
- syft.path,
1783
- ['scan', 'registry:' + imageReference, '--output', 'cyclonedx-json'],
1784
- 'Exact platform OCI SBOM',
1785
- { env: { SYFT_CHECK_FOR_APP_UPDATE: 'false' } },
1786
- ),
1787
- );
2910
+ const rawSbom = reusable
2911
+ ? reusable.sbomBytes.toString('utf8')
2912
+ : String(
2913
+ execute(
2914
+ syft.path,
2915
+ ['scan', 'registry:' + imageReference, '--output', 'cyclonedx-json'],
2916
+ 'Exact platform OCI SBOM',
2917
+ { env: { SYFT_CHECK_FOR_APP_UPDATE: 'false' } },
2918
+ ),
2919
+ );
1788
2920
  let sbom;
1789
2921
  try {
1790
2922
  sbom = parseCycloneDx(
@@ -1796,14 +2928,20 @@ if (mode === 'platform') {
1796
2928
  }
1797
2929
  const componentSet = componentSetFor(sbom);
1798
2930
  if (
1799
- componentSet.some(
2931
+ sbom.components.some(
1800
2932
  (component) =>
1801
- typeof component.name !== 'string' ||
2933
+ typeof component?.name !== 'string' ||
2934
+ component.name.length === 0 ||
1802
2935
  typeof component.type !== 'string' ||
1803
- typeof component.version !== 'string',
2936
+ component.type.length === 0 ||
2937
+ !(
2938
+ (typeof component.version === 'string' &&
2939
+ component.version.length > 0) ||
2940
+ isVersionlessSyftFileComponent(component)
2941
+ ),
1804
2942
  )
1805
2943
  ) {
1806
- throw new Error('Platform SBOM contains invalid or development components.');
2944
+ throw new Error('Platform SBOM contains an invalid component identity.');
1807
2945
  }
1808
2946
  const serializedSbom = JSON.stringify(sbom, null, 2) + '\\n';
1809
2947
  const relativeSbomPath =
@@ -1842,6 +2980,7 @@ if (mode === 'platform') {
1842
2980
  evidence,
1843
2981
  );
1844
2982
  }
2983
+ await yieldToReleaseSignals();
1845
2984
  process.stdout.write(
1846
2985
  JSON.stringify({
1847
2986
  artifactCount: containerInputs.length,
@@ -1850,12 +2989,33 @@ if (mode === 'platform') {
1850
2989
  stage,
1851
2990
  }) + '\\n',
1852
2991
  );
2992
+ } catch (error) {
2993
+ platformError = error;
1853
2994
  } finally {
1854
- rmSync(syft.temporary, { force: true, recursive: true });
1855
- if (crane) {
1856
- rmSync(crane.temporary, { force: true, recursive: true });
2995
+ const cleanupErrors = [];
2996
+ for (const cleanup of [
2997
+ () => cleanupReleaseBuilder(),
2998
+ ...(syft
2999
+ ? [() => removeExactReleaseToolTemporaryDirectory(syft.temporary)]
3000
+ : []),
3001
+ ...(crane
3002
+ ? [() => removeExactReleaseToolTemporaryDirectory(crane.temporary)]
3003
+ : []),
3004
+ ]) {
3005
+ try {
3006
+ cleanup();
3007
+ } catch (error) {
3008
+ cleanupErrors.push(error);
3009
+ }
3010
+ }
3011
+ if (cleanupErrors.length > 0) {
3012
+ throw new AggregateError(
3013
+ [...(platformError ? [platformError] : []), ...cleanupErrors],
3014
+ 'Container platform release and independent owned-resource cleanup failed.',
3015
+ );
1857
3016
  }
1858
3017
  }
3018
+ if (platformError) throw platformError;
1859
3019
  process.exit(0);
1860
3020
  }
1861
3021
 
@@ -2067,6 +3227,7 @@ const output = resolve(
2067
3227
  slice,
2068
3228
  'binding.json',
2069
3229
  );
3230
+ await yieldToReleaseSignals();
2070
3231
  immutableWrite(output, binding);
2071
3232
  process.stdout.write(
2072
3233
  JSON.stringify({
@@ -2079,7 +3240,7 @@ process.stdout.write(
2079
3240
  );
2080
3241
  } finally {
2081
3242
  if (assemblyCrane) {
2082
- rmSync(assemblyCrane.temporary, { force: true, recursive: true });
3243
+ removeExactReleaseToolTemporaryDirectory(assemblyCrane.temporary);
2083
3244
  }
2084
3245
  }
2085
3246
  `;