@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
@@ -7,19 +7,27 @@ exports.renderWorkspaceFiles = renderWorkspaceFiles;
7
7
  exports.renderWorkspaceRootFilePlan = renderWorkspaceRootFilePlan;
8
8
  exports.renderFoundationFilePlan = renderFoundationFilePlan;
9
9
  const sensitive_patterns_1 = require("@empire-builder-kit/runtime/sensitive-patterns");
10
+ const dependency_overrides_js_1 = require("./dependency-overrides.js");
10
11
  const index_js_1 = require("../../ownership/index.js");
11
12
  const index_js_2 = require("../../frontend-security/index.js");
13
+ const capability_plan_js_1 = require("../../foundation/capability-plan.js");
14
+ const cost_estimate_js_1 = require("../../foundation/cost-estimate.js");
12
15
  const index_js_3 = require("../../git-policy/index.js");
13
16
  const index_js_4 = require("../../repository-policy/index.js");
14
17
  const index_js_5 = require("../../release/index.js");
15
18
  const index_js_6 = require("../../supply-chain/index.js");
16
19
  const deterministic_order_js_1 = require("../../deterministic-order.js");
17
20
  const tool_versions_js_1 = require("./tool-versions.js");
21
+ const workspace_files_shared_operation_claims_js_1 = require("./workspace-files-shared-operation-claims.js");
18
22
  const workspace_files_developer_agent_js_1 = require("./workspace-files-developer-agent.js");
19
- const workspace_files_foundation_workload_roles_js_1 = require("./workspace-files-foundation-workload-roles.js");
20
- const workspace_files_performance_cost_js_1 = require("./workspace-files-performance-cost.js");
21
- const workspace_files_performance_load_js_1 = require("./workspace-files-performance-load.js");
22
- const workspace_files_workload_role_baseline_js_1 = require("./workspace-files-workload-role-baseline.js");
23
+ const workspace_files_ci_acceptance_js_1 = require("./workspace-files-ci-acceptance.js");
24
+ const workspace_files_ci_install_js_1 = require("./workspace-files-ci-install.js");
25
+ const workspace_files_adopter_skills_js_1 = require("./workspace-files-adopter-skills.js");
26
+ const workspace_files_ecr_inspector_js_1 = require("./workspace-files-ecr-inspector.js");
27
+ const workspace_files_thin_deploy_js_1 = require("./workspace-files-thin-deploy.js");
28
+ const workspace_files_thin_docs_js_1 = require("./workspace-files-thin-docs.js");
29
+ const workspace_files_thin_release_js_1 = require("./workspace-files-thin-release.js");
30
+ const workspace_files_stage_authority_js_1 = require("./workspace-files-stage-authority.js");
23
31
  const GENERATED_CREDENTIAL_SHAPE_PATTERNS = `[${sensitive_patterns_1.CREDENTIAL_SHAPE_DEFINITIONS.map(({ pattern }) => `new RegExp(${JSON.stringify(pattern.source)}, ${JSON.stringify(pattern.flags)})`).join(',')}]`;
24
32
  const GENERATED_RELEASE_ROLLOUT_PLAN_ID_PATTERN = JSON.stringify(index_js_5.RELEASE_ROLLOUT_PLAN_ID_PATTERN_SOURCE);
25
33
  exports.BLUEPRINT_TRUST_AUTHORITY_PATH = 'tools/blueprint-trust-check.mjs';
@@ -60,11 +68,12 @@ exports.PLAN_PROVIDER_REFRESH_GLOBAL_READ_ACTIONS = [
60
68
  'rds:DescribeDBProxies',
61
69
  'rds:DescribeDBSubnetGroups',
62
70
  ];
63
- function validatePlanProviderRefreshPolicy(value, region, account) {
71
+ function validatePlanProviderRefreshPolicy(value, region, account, stage) {
64
72
  if (!Array.isArray(value) ||
65
73
  value.length === 0 ||
66
74
  !/^[a-z]{2}(?:-gov)?-[a-z]+-\d$/.test(region) ||
67
- !/^\d{12}$/.test(account)) {
75
+ !/^\d{12}$/.test(account) ||
76
+ !['production', 'staging'].includes(stage)) {
68
77
  throw new Error('Provider refresh policy must be a non-empty exact statement array for one account and region.');
69
78
  }
70
79
  const allowed = new Set(exports.PLAN_PROVIDER_REFRESH_READ_ACTIONS);
@@ -117,6 +126,14 @@ function validatePlanProviderRefreshPolicy(value, region, account) {
117
126
  if (resources.some((resource) => {
118
127
  const arn = resource.split(':');
119
128
  const target = arn.slice(5).join(':');
129
+ const boundedPathWildcard = target.endsWith('/*') && !/[*?]/.test(target.slice(0, -2));
130
+ const boundedLambdaStagePrefix = arn[2] === 'lambda' &&
131
+ actions.every((action) => action.startsWith('lambda:') && !global.has(action)) &&
132
+ new RegExp('^function:(?:' +
133
+ stage +
134
+ '-|[A-Za-z0-9][A-Za-z0-9_-]*-' +
135
+ stage +
136
+ '-)[A-Za-z0-9_-]*\\*$').test(target);
120
137
  return (arn[0] !== 'arn' ||
121
138
  arn[1] !== 'aws' ||
122
139
  !/^[a-z0-9-]+$/.test(arn[2] ?? '') ||
@@ -124,7 +141,8 @@ function validatePlanProviderRefreshPolicy(value, region, account) {
124
141
  !['', account].includes(arn[4] ?? '') ||
125
142
  !target ||
126
143
  (/[*?]/.test(target) &&
127
- (!target.endsWith('/*') || /[*?]/.test(target.slice(0, -2)))));
144
+ !boundedPathWildcard &&
145
+ !boundedLambdaStagePrefix));
128
146
  })) {
129
147
  throw new Error(`Provider refresh statement ${index} is not bounded to exact targets in the selected account and region.`);
130
148
  }
@@ -161,6 +179,51 @@ const TOOL_VERSIONS = {
161
179
  function json(value) {
162
180
  return `${JSON.stringify(value, null, 2)}\n`;
163
181
  }
182
+ function indentWorkflowScript(source, spaces) {
183
+ const prefix = ' '.repeat(spaces);
184
+ return source
185
+ .trim()
186
+ .split('\n')
187
+ .map((line) => `${prefix}${line}`)
188
+ .join('\n');
189
+ }
190
+ function protectedSecretPolicyDerivationScript() {
191
+ return `node --input-type=module <<'NODE'
192
+ import { appendFileSync } from 'node:fs';
193
+ import {
194
+ configuredDeploymentAccountForStage,
195
+ configuredDeploymentRegion,
196
+ isActiveDeploymentStage,
197
+ readRepositoryOperatingPolicy,
198
+ } from '@empire-builder-kit/nx/repository-policy';
199
+
200
+ const output = process.env.GITHUB_OUTPUT?.trim() ?? '';
201
+ const renderedProfile = process.env.EBK_RENDERED_PROFILE?.trim() ?? '';
202
+ const stage = process.env.EBK_STAGE?.trim() ?? '';
203
+ if (!output) throw new Error('Secret workflow authority output is unavailable.');
204
+ const policy = readRepositoryOperatingPolicy(process.cwd());
205
+ if (policy.deploymentProfile !== renderedProfile) {
206
+ throw new Error('Generated and committed deployment profiles differ.');
207
+ }
208
+ if (!isActiveDeploymentStage(policy.deploymentProfile, stage)) {
209
+ throw new Error('Secret workflow stage is inactive for the committed deployment profile.');
210
+ }
211
+ configuredDeploymentAccountForStage(
212
+ policy.deploymentProfile,
213
+ policy.deploymentAccounts,
214
+ stage,
215
+ );
216
+ const accounts = policy.deploymentAccounts;
217
+ const region = configuredDeploymentRegion(policy.deploymentRegion);
218
+ appendFileSync(output, [
219
+ 'deployment_region=' + region,
220
+ 'development_account_id=' + (accounts.development ?? ''),
221
+ 'staging_account_id=' + (accounts.staging ?? ''),
222
+ 'production_account_id=' + (accounts.production ?? ''),
223
+ '',
224
+ ].join('\\n'));
225
+ NODE`;
226
+ }
164
227
  function planApplicationBindingCheck() {
165
228
  return ` node - <<'NODE'
166
229
  const declared = JSON.parse(process.env.AWS_PLAN_SST_APPS_JSON ?? '');
@@ -474,6 +537,8 @@ function planRoleAuditStep(branch, mode = 'produce') {
474
537
  EBK_PLAN_ENVIRONMENT: infrastructure-plan-${stage}
475
538
  EBK_PLAN_ROLE_ARN: \${{ env.AWS_PLAN_ROLE_ARN }}
476
539
  EBK_PLAN_REPOSITORY: \${{ github.repository }}
540
+ EBK_PLAN_REPOSITORY_ID: \${{ github.repository_id }}
541
+ EBK_PLAN_REPOSITORY_OWNER_ID: \${{ github.repository_owner_id }}
477
542
  run: |
478
543
  role_name="\${EBK_PLAN_ROLE_ARN##*/}"
479
544
  test "$(aws sts get-caller-identity --query Account --output text)" = "$AWS_EXPECTED_ACCOUNT_ID" ||
@@ -506,8 +571,23 @@ function planRoleAuditStep(branch, mode = 'produce') {
506
571
  const role = JSON.parse(readFileSync(process.argv[2], 'utf8')).Role;
507
572
  const policy = JSON.parse(readFileSync(process.argv[3], 'utf8')).PolicyDocument;
508
573
  const account = process.env.AWS_EXPECTED_ACCOUNT_ID;
574
+ const repository = process.env.EBK_PLAN_REPOSITORY ?? '';
575
+ const repositoryId = process.env.EBK_PLAN_REPOSITORY_ID ?? '';
576
+ const repositoryOwnerId = process.env.EBK_PLAN_REPOSITORY_OWNER_ID ?? '';
509
577
  const stateBucketArn = process.env.AWS_PLAN_STATE_BUCKET_ARN;
510
578
  const stateKmsKeyArn = process.env.AWS_PLAN_STATE_KMS_KEY_ARN;
579
+ if (
580
+ !/^[A-Za-z0-9](?:[A-Za-z0-9-]{0,38})\\/[A-Za-z0-9_.-]{1,100}$/.test(
581
+ repository,
582
+ ) ||
583
+ !/^[1-9][0-9]*$/.test(repositoryOwnerId) ||
584
+ !/^[1-9][0-9]*$/.test(repositoryId)
585
+ ) throw new Error('Exact immutable GitHub repository identity is required.');
586
+ const [repositoryOwner, repositoryName] = repository.split('/');
587
+ const oidcSubject =
588
+ 'repo:' + repositoryOwner + '@' + repositoryOwnerId + '/' +
589
+ repositoryName + '@' + repositoryId + ':environment:' +
590
+ process.env.EBK_PLAN_ENVIRONMENT;
511
591
  const parseExactArnArray = (name, pattern) => {
512
592
  let value;
513
593
  try {
@@ -551,6 +631,7 @@ function planRoleAuditStep(branch, mode = 'produce') {
551
631
  'AWS_PLAN_GENERATED_SECRET_ARNS_JSON',
552
632
  new RegExp('^arn:aws:secretsmanager:' + process.env.AWS_REGION + ':' + account + ':secret:ebk/'),
553
633
  );
634
+ const stateStage = process.env.EBK_PLAN_BRANCH === 'main' ? 'production' : 'staging';
554
635
  let providerRefreshPolicy;
555
636
  try {
556
637
  providerRefreshPolicy = JSON.parse(
@@ -614,6 +695,23 @@ function planRoleAuditStep(branch, mode = 'produce') {
614
695
  for (const resource of resources) {
615
696
  const arn = resource.split(':');
616
697
  const target = arn.slice(5).join(':');
698
+ const boundedPathWildcard =
699
+ target.endsWith('/*') &&
700
+ !/[*?]/.test(target.slice(0, -2));
701
+ const boundedLambdaStagePrefix =
702
+ arn[2] === 'lambda' &&
703
+ actions.every(
704
+ (action) =>
705
+ action.startsWith('lambda:') &&
706
+ !globalProviderReadActions.has(action),
707
+ ) &&
708
+ new RegExp(
709
+ '^function:(?:' +
710
+ stateStage +
711
+ '-|[A-Za-z0-9][A-Za-z0-9_-]*-' +
712
+ stateStage +
713
+ '-)[A-Za-z0-9_-]*\\\\*$',
714
+ ).test(target);
617
715
  if (
618
716
  arn[0] !== 'arn' ||
619
717
  arn[1] !== 'aws' ||
@@ -622,7 +720,8 @@ function planRoleAuditStep(branch, mode = 'produce') {
622
720
  !['', account].includes(arn[4] ?? '') ||
623
721
  !target ||
624
722
  (/[*?]/.test(target) &&
625
- (!target.endsWith('/*') || /[*?]/.test(target.slice(0, -2))))
723
+ !boundedPathWildcard &&
724
+ !boundedLambdaStagePrefix)
626
725
  ) throw new Error('Provider refresh statement ' + index + ' has a target outside the selected account and region.');
627
726
  }
628
727
  }
@@ -637,7 +736,6 @@ function planRoleAuditStep(branch, mode = 'produce') {
637
736
  ).sort((left, right) =>
638
737
  left.Sid < right.Sid ? -1 : left.Sid > right.Sid ? 1 : 0,
639
738
  );
640
- const stateStage = process.env.EBK_PLAN_BRANCH === 'main' ? 'production' : 'staging';
641
739
  if (
642
740
  stateKmsKeyArn &&
643
741
  stateKmsKeyArn !==
@@ -681,7 +779,7 @@ function planRoleAuditStep(branch, mode = 'produce') {
681
779
  Action: 'sts:AssumeRoleWithWebIdentity',
682
780
  Condition: { StringEquals: {
683
781
  'token.actions.githubusercontent.com:aud': 'sts.amazonaws.com',
684
- 'token.actions.githubusercontent.com:sub': 'repo:' + process.env.EBK_PLAN_REPOSITORY + ':environment:' + process.env.EBK_PLAN_ENVIRONMENT,
782
+ 'token.actions.githubusercontent.com:sub': oidcSubject,
685
783
  }},
686
784
  }],
687
785
  };
@@ -703,11 +801,24 @@ function planRoleAuditStep(branch, mode = 'produce') {
703
801
  Sid: 'ObserveInfrastructureIdentity',
704
802
  Effect: 'Allow',
705
803
  Action: [
706
- 'resourcegroupstaggingapi:GetResources',
804
+ 'tag:GetResources',
707
805
  'sts:GetCallerIdentity',
708
806
  ],
709
807
  Resource: '*',
710
808
  },
809
+ {
810
+ Sid: 'ObserveStageSecretVersionIdentity',
811
+ Effect: 'Allow',
812
+ Action: 'secretsmanager:ListSecretVersionIds',
813
+ Resource:
814
+ 'arn:aws:secretsmanager:' + process.env.AWS_REGION + ':' +
815
+ account + ':secret:*',
816
+ Condition: {
817
+ StringEquals: {
818
+ 'aws:ResourceTag/ebk:stage': stateStage,
819
+ },
820
+ },
821
+ },
711
822
  {
712
823
  Sid: 'ValidatePlannedIamPolicies',
713
824
  Effect: 'Allow',
@@ -782,7 +893,10 @@ function planRoleAuditStep(branch, mode = 'produce') {
782
893
  generatedSecretArns,
783
894
  providerRefreshStatements,
784
895
  region: process.env.AWS_REGION,
785
- repository: process.env.EBK_PLAN_REPOSITORY,
896
+ repository,
897
+ repositoryId,
898
+ repositoryOwnerId,
899
+ oidcSubject,
786
900
  role: {
787
901
  arn: role.Arn,
788
902
  policy: canonical(policy),
@@ -826,11 +940,17 @@ function planRoleAuditJob(stage, configureAwsAction) {
826
940
  const production = stage === 'production';
827
941
  const branch = production ? 'main' : 'develop';
828
942
  const prefix = production ? 'AWS_PRODUCTION_PLAN' : 'AWS_STAGING_PLAN';
943
+ const profileAdmission = production
944
+ ? ''
945
+ : ` (github.event_name == 'workflow_dispatch' ||\n needs.deployment-mode.outputs.profile != 'starter') &&\n`;
829
946
  return ` infrastructure-plan-role-audit-${stage}:
830
947
  name: audit ${stage} infrastructure plan role
831
- needs: route
948
+ needs: [route, deployment-mode]
832
949
  if: |
833
- github.ref == 'refs/heads/${branch}' &&
950
+ always() &&
951
+ needs.route.result == 'success' &&
952
+ needs.deployment-mode.result == 'success' &&
953
+ ${profileAdmission} github.ref == 'refs/heads/${branch}' &&
834
954
  (github.event_name == 'push' && 'deploy' || inputs.action) != 'unlock'
835
955
  runs-on: ubuntu-latest
836
956
  environment: infrastructure-plan-${stage}
@@ -938,6 +1058,7 @@ const SUPPLY_CHAIN_WORKSPACE_FILE_PLAN = (0, index_js_6.renderSupplyChainWorkspa
938
1058
  generatedAt: '1970-01-01T00:00:00.000Z',
939
1059
  });
940
1060
  const WORKSPACE_SEEDED_PATHS = new Set([
1061
+ dependency_overrides_js_1.DEPENDENCY_OVERRIDES_PATH,
941
1062
  ...SUPPLY_CHAIN_WORKSPACE_FILE_PLAN.seeded,
942
1063
  ...workspace_files_developer_agent_js_1.DEVELOPER_AGENT_WORKSPACE_SEEDED_PATHS,
943
1064
  '.github/CODEOWNERS',
@@ -947,7 +1068,10 @@ const WORKSPACE_SEEDED_PATHS = new Set([
947
1068
  'docs/architecture.md',
948
1069
  'docs/git-workflow.md',
949
1070
  'docs/repository-operating-policy.md',
1071
+ 'docs/stage-deployment-authority.md',
1072
+ 'docs/workspace-deployment-ownership.md',
950
1073
  'docs/work-markers.md',
1074
+ workspace_files_shared_operation_claims_js_1.SHARED_OPERATION_CLAIMS_PATH,
951
1075
  'ebk.repository-policy.json',
952
1076
  ]);
953
1077
  exports.WORKSPACE_PACKAGE_MANAGED_JSON_POINTERS = [
@@ -964,21 +1088,23 @@ exports.WORKSPACE_PROJECT_MANAGED_JSON_POINTERS = [
964
1088
  '/root',
965
1089
  '/tags',
966
1090
  '/targets/blueprint-trust-check',
1091
+ '/targets/blueprint-trust-check-main',
967
1092
  '/targets/blueprint-trust-check-protected',
968
1093
  '/targets/developer-context',
969
1094
  '/targets/deployment-plan',
1095
+ '/targets/deploy-local',
970
1096
  '/targets/dev',
971
1097
  '/targets/dev-doctor',
972
1098
  '/targets/dev-setup',
973
1099
  '/targets/github-bootstrap',
974
1100
  '/targets/install-git-hooks',
1101
+ '/targets/stage-authority-render',
975
1102
  '/targets/supply-chain-check',
976
1103
  '/targets/supply-chain-check-protected',
977
1104
  '/targets/supply-chain-scan',
978
1105
  '/targets/verify',
1106
+ '/targets/verify-proposed',
979
1107
  '/targets/work-markers',
980
- '/targets/workload-role-candidate-report',
981
- '/targets/workload-role-provisioner-build',
982
1108
  '/targets/workspace-health',
983
1109
  ];
984
1110
  const FOUNDATION_SEEDED_PATHS = new Set([
@@ -986,6 +1112,10 @@ const FOUNDATION_SEEDED_PATHS = new Set([
986
1112
  'docs/costs.md',
987
1113
  'docs/runbooks.md',
988
1114
  'docs/security.md',
1115
+ 'performance/account-cost-policy.json',
1116
+ 'security/router-waf-blocking-rehearsal.json',
1117
+ 'security/router-waf-live-evidence.json',
1118
+ 'security/router-waf.json',
989
1119
  ]);
990
1120
  function createFoundationOwnership(files, frameworkVersion) {
991
1121
  const paths = Object.keys(files)
@@ -1048,7 +1178,48 @@ function createWorkspaceOwnership(files, frameworkVersion) {
1048
1178
  }
1049
1179
  function renderWorkspaceFiles(options) {
1050
1180
  const { frameworkVersion, name } = options;
1181
+ const foundationPlan = (0, capability_plan_js_1.planFoundationCapabilities)({
1182
+ current: options.foundationDescriptor,
1183
+ requirements: options.foundationRequirements ?? [],
1184
+ });
1185
+ const dependencyOverrides = (0, dependency_overrides_js_1.parseDependencyOverrides)(options.dependencyOverrides === undefined
1186
+ ? { schemaVersion: 1, overrides: {} }
1187
+ : options.dependencyOverrides);
1188
+ const deploymentProfile = options.deploymentProfile ?? 'starter';
1189
+ const deliveryLane = options.deliveryLane ?? 'protected-pair';
1190
+ if (deliveryLane === 'trunk' && deploymentProfile !== 'starter') {
1191
+ throw new Error('The trunk delivery lane requires the starter deployment profile.');
1192
+ }
1193
+ const trunkLane = deliveryLane === 'trunk';
1194
+ const productionApprovalMode = options.productionApprovalMode ?? 'promotion-pr';
1195
+ if (productionApprovalMode === 'trunk-promotion' &&
1196
+ (deploymentProfile === 'starter' || trunkLane)) {
1197
+ throw new Error('trunk-promotion requires an isolated deployment profile on the protected pair.');
1198
+ }
1199
+ const trunkPromotion = productionApprovalMode === 'trunk-promotion';
1200
+ const protectedBranches = trunkLane ? '[main]' : '[develop, main]';
1201
+ const activeStages = (0, index_js_4.activeDeploymentStages)(deploymentProfile);
1202
+ const protectedWorkflowStageChoices = deploymentProfile === 'starter'
1203
+ ? 'development, production'
1204
+ : deploymentProfile === 'production-isolated'
1205
+ ? 'production'
1206
+ : 'staging, production';
1207
+ const ecrInspectorWorkflowStageChoices = deploymentProfile === 'starter'
1208
+ ? 'production'
1209
+ : protectedWorkflowStageChoices;
1210
+ const rehearsalStage = (0, index_js_4.productionRehearsalStage)(deploymentProfile);
1211
+ const protectedWorkflowStageBranchCases = `${rehearsalStage}:refs/heads/develop|production:refs/heads/main`;
1212
+ const workspaceDeploymentId = options.workspaceDeploymentId ??
1213
+ `ebkws-${(0, index_js_1.hashGeneratedContent)(`workspace-deployment-id:${name}`).slice(0, 32)}`;
1214
+ const initialRepositoryPolicy = (0, index_js_4.createIncubationRepositoryPolicy)(deploymentProfile, workspaceDeploymentId);
1215
+ initialRepositoryPolicy.deploymentAccounts = options.deploymentAccounts ?? {
1216
+ development: null,
1217
+ production: null,
1218
+ staging: null,
1219
+ };
1220
+ initialRepositoryPolicy.deploymentRegion = options.deploymentRegion ?? null;
1051
1221
  const developerAgentFilePlan = (0, workspace_files_developer_agent_js_1.renderDeveloperAgentWorkspaceFilePlan)({
1222
+ deploymentProfile,
1052
1223
  frameworkVersion,
1053
1224
  name,
1054
1225
  });
@@ -1059,7 +1230,38 @@ function renderWorkspaceFiles(options) {
1059
1230
  const pnpmAction = (0, index_js_3.pinnedAction)(index_js_3.GITHUB_ACTION_PINS.pnpmSetup);
1060
1231
  const setupNodeAction = (0, index_js_3.pinnedAction)(index_js_3.GITHUB_ACTION_PINS.setupNode);
1061
1232
  const uploadArtifactAction = (0, index_js_3.pinnedAction)(index_js_3.GITHUB_ACTION_PINS.uploadArtifact);
1233
+ const cacheAction = (0, index_js_3.pinnedAction)(index_js_3.GITHUB_ACTION_PINS.cache);
1234
+ const nxSetShasAction = (0, index_js_3.pinnedAction)(index_js_3.GITHUB_ACTION_PINS.nxSetShas);
1235
+ // GitHub scopes cache reads to the current branch and its base, so protected
1236
+ // pushes never restore entries written by pull-request runs.
1237
+ const nxCacheSteps = ` - name: Restore the Nx task cache
1238
+ uses: ${cacheAction}
1239
+ with:
1240
+ path: .nx/cache
1241
+ key: nx-\${{ runner.os }}-\${{ hashFiles('pnpm-lock.yaml') }}-\${{ github.sha }}
1242
+ restore-keys: |
1243
+ nx-\${{ runner.os }}-\${{ hashFiles('pnpm-lock.yaml') }}-`;
1244
+ // Isolated profiles bind the staging release candidate when promoting to
1245
+ // production, so a main push never needs a rebuilt candidate.
1246
+ const releaseCandidateBranchCondition = deploymentProfile === 'starter'
1247
+ ? ''
1248
+ : " &&\n github.ref != 'refs/heads/main'";
1249
+ // In the trunk lane no pull request is a protected route: main is the only
1250
+ // protected branch and every feature pull request targets it.
1251
+ const protectedDevelopPromotion = trunkLane
1252
+ ? 'false'
1253
+ : "github.event.pull_request.head.repo.full_name == github.repository && github.event.pull_request.head.ref == 'develop' && github.event.pull_request.base.ref == 'main'";
1254
+ const protectedMainSync = trunkLane
1255
+ ? 'false'
1256
+ : "github.event.pull_request.head.repo.full_name == github.repository && github.event.pull_request.head.ref == 'main' && github.event.pull_request.base.ref == 'develop'";
1257
+ const protectedPush = trunkLane
1258
+ ? "github.event_name == 'push' && github.ref == 'refs/heads/main'"
1259
+ : "github.event_name == 'push' && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/develop')";
1260
+ const protectedDeploymentRouteJobName = 'protected deployment route';
1261
+ const stagingApplyJobPrefix = 'apply staging plan ';
1062
1262
  const files = {
1263
+ ...(0, workspace_files_ci_acceptance_js_1.renderWorkspaceCiAcceptanceFiles)(),
1264
+ ...(0, workspace_files_ci_install_js_1.renderWorkspaceCiInstallFiles)(),
1063
1265
  '.node-version': `${tool_versions_js_1.WORKSPACE_NODE_VERSION}\n`,
1064
1266
  '.github/CODEOWNERS': `# Replace this placeholder with the GitHub user or team that owns protected source.
1065
1267
  # GitHub bootstrap remains blocked until a non-placeholder catch-all owner is committed.
@@ -1069,21 +1271,83 @@ function renderWorkspaceFiles(options) {
1069
1271
 
1070
1272
  on:
1071
1273
  pull_request:
1072
- branches: [develop, main]
1274
+ branches: ${protectedBranches}
1073
1275
  push:
1074
- branches: [develop, main]
1276
+ branches: ${protectedBranches}
1075
1277
  workflow_dispatch:
1076
1278
 
1077
1279
  concurrency:
1078
- group: verify-\${{ github.ref }}
1079
- cancel-in-progress: true
1280
+ group: verify-\${{ github.event_name == 'push' && github.sha || github.ref }}
1281
+ cancel-in-progress: \${{ github.event_name != 'push' }}
1080
1282
 
1081
1283
  permissions:
1284
+ actions: read
1082
1285
  contents: read
1286
+ pull-requests: read
1287
+
1288
+ env:
1289
+ EBK_CI_CANDIDATE_SHA: \${{ github.event.pull_request.head.sha || github.sha }}
1083
1290
 
1084
1291
  jobs:
1292
+ acceptance:
1293
+ name: accepted merge evidence
1294
+ if: ${protectedPush}
1295
+ runs-on: ubuntu-latest
1296
+ outputs:
1297
+ history_only: \${{ steps.resolve.outputs.history_only == 'true' && steps.restore.outcome == 'success' && steps.verify.outcome == 'success' }}
1298
+ reused: \${{ steps.resolve.outputs.reused == 'true' && steps.restore.outcome == 'success' && steps.verify.outcome == 'success' }}
1299
+ route: \${{ steps.resolve.outputs.reused == 'true' && steps.restore.outcome == 'success' && steps.verify.outcome == 'success' && steps.resolve.outputs.route || '' }}
1300
+ steps:
1301
+ - name: Check out immutable merged candidate
1302
+ if: ${protectedPush}
1303
+ uses: ${checkoutAction}
1304
+ with:
1305
+ fetch-depth: 0
1306
+ ref: \${{ github.sha }}
1307
+ - name: Resolve exact protected-route acceptance
1308
+ if: ${protectedPush}
1309
+ id: resolve
1310
+ env:
1311
+ GH_TOKEN: \${{ github.token }}
1312
+ run: node tools/ci/workspace-acceptance.mjs resolve
1313
+ - name: Restore exact protected-route acceptance
1314
+ if: steps.resolve.outputs.reused == 'true'
1315
+ id: restore
1316
+ continue-on-error: true
1317
+ env:
1318
+ EBK_ACCEPTANCE_ARTIFACT_DIGEST: \${{ steps.resolve.outputs.artifact_digest }}
1319
+ EBK_ACCEPTANCE_ARTIFACT_ID: \${{ steps.resolve.outputs.artifact_id }}
1320
+ EBK_ACCEPTANCE_ARTIFACT_SIZE: \${{ steps.resolve.outputs.artifact_size }}
1321
+ GH_TOKEN: \${{ github.token }}
1322
+ run: >-
1323
+ node tools/ci/workspace-acceptance.mjs download
1324
+ .ebk/evidence/reused-acceptance
1325
+ "$EBK_ACCEPTANCE_ARTIFACT_ID"
1326
+ "$EBK_ACCEPTANCE_ARTIFACT_DIGEST"
1327
+ "$EBK_ACCEPTANCE_ARTIFACT_SIZE"
1328
+ - name: Verify exact merged-tree acceptance
1329
+ if: steps.resolve.outputs.reused == 'true' && steps.restore.outcome == 'success'
1330
+ id: verify
1331
+ continue-on-error: true
1332
+ env:
1333
+ EBK_ACCEPTANCE_BASE: \${{ steps.resolve.outputs.base_commit }}
1334
+ EBK_ACCEPTANCE_HEAD: \${{ steps.resolve.outputs.head_commit }}
1335
+ EBK_ACCEPTANCE_ROUTE: \${{ steps.resolve.outputs.route }}
1336
+ EBK_ACCEPTANCE_RUN_ATTEMPT: \${{ steps.resolve.outputs.run_attempt }}
1337
+ EBK_ACCEPTANCE_RUN_ID: \${{ steps.resolve.outputs.run_id }}
1338
+ run: >-
1339
+ node tools/ci/workspace-acceptance.mjs verify
1340
+ .ebk/evidence/reused-acceptance/manifest.json
1341
+ "$EBK_ACCEPTANCE_HEAD"
1342
+ "$EBK_ACCEPTANCE_BASE"
1343
+ "$EBK_ACCEPTANCE_ROUTE"
1344
+ "$EBK_ACCEPTANCE_RUN_ID"
1345
+ "$EBK_ACCEPTANCE_RUN_ATTEMPT"
1346
+
1085
1347
  policy:
1086
1348
  name: policy
1349
+ needs: acceptance
1350
+ if: always() && needs.acceptance.outputs.reused != 'true'
1087
1351
  runs-on: ubuntu-latest
1088
1352
  steps:
1089
1353
  - uses: ${checkoutAction}
@@ -1095,31 +1359,118 @@ jobs:
1095
1359
  with:
1096
1360
  node-version: ${tool_versions_js_1.WORKSPACE_NODE_VERSION}
1097
1361
  cache: pnpm
1098
- - run: pnpm install --frozen-lockfile
1362
+ - run: node tools/ci/install.mjs
1099
1363
  - run: node tools/ebk-git-policy.mjs
1364
+ env:
1365
+ EBK_HEAD_REPOSITORY: \${{ github.event.pull_request.head.repo.full_name }}
1100
1366
 
1101
1367
  verify:
1102
1368
  name: verify
1369
+ needs: acceptance
1370
+ if: always() && needs.acceptance.outputs.reused != 'true'
1103
1371
  runs-on: ubuntu-latest
1104
1372
  steps:
1105
1373
  - uses: ${checkoutAction}
1374
+ with:
1375
+ fetch-depth: 0
1376
+ ref: \${{ github.event.pull_request.head.sha || github.sha }}
1106
1377
  - uses: ${pnpmAction}
1107
1378
  - uses: ${setupNodeAction}
1108
1379
  with:
1109
1380
  node-version: ${tool_versions_js_1.WORKSPACE_NODE_VERSION}
1110
1381
  cache: pnpm
1111
- - run: pnpm install --frozen-lockfile
1112
- - run: pnpm nx format:check
1113
- - run: pnpm nx run-many -t verify --parallel=3
1382
+ - run: node tools/ci/install.mjs
1383
+ ${nxCacheSteps}
1384
+ - run: pnpm format:check
1385
+ - name: Resolve the affected base and head for proposed work
1386
+ if: github.event_name == 'pull_request' && !(${protectedDevelopPromotion} || ${protectedMainSync})
1387
+ uses: ${nxSetShasAction}
1388
+ - name: Verify proposed workspace
1389
+ if: github.event_name == 'pull_request' && !(${protectedDevelopPromotion} || ${protectedMainSync})
1390
+ run: |
1391
+ pnpm nx run ${name}:verify-proposed
1392
+ pnpm nx affected -t verify --exclude=${name} --parallel=3
1393
+ - name: Verify protected develop promotion
1394
+ if: github.event_name == 'pull_request' && ${protectedDevelopPromotion}
1395
+ run: pnpm nx run-many -t verify --parallel=3
1396
+ - name: Verify protected main sync
1397
+ if: github.event_name == 'pull_request' && ${protectedMainSync}
1398
+ run: |
1399
+ pnpm nx run ${name}:blueprint-trust-check-main
1400
+ pnpm nx run ${name}:verify-proposed
1401
+ pnpm nx run-many -t verify --exclude=${name} --parallel=3
1402
+ - name: Verify adopted workspace
1403
+ if: github.event_name != 'pull_request'
1404
+ run: pnpm nx run-many -t verify --parallel=3
1405
+
1406
+ seal-acceptance:
1407
+ name: seal protected route acceptance
1408
+ needs: [policy, verify]
1409
+ if: >-
1410
+ always() &&
1411
+ needs.policy.result == 'success' &&
1412
+ needs.verify.result == 'success' &&
1413
+ github.event_name == 'pull_request' &&
1414
+ (${protectedDevelopPromotion} || ${protectedMainSync})
1415
+ runs-on: ubuntu-latest
1416
+ steps:
1417
+ - name: Check out immutable accepted candidate
1418
+ uses: ${checkoutAction}
1419
+ with:
1420
+ fetch-depth: 0
1421
+ ref: \${{ env.EBK_CI_CANDIDATE_SHA }}
1422
+ - name: Seal exact protected-route acceptance
1423
+ id: seal
1424
+ env:
1425
+ EBK_BASE_COMMIT: \${{ github.event.pull_request.base.sha }}
1426
+ EBK_HEAD_REPOSITORY: \${{ github.event.pull_request.head.repo.full_name }}
1427
+ run: >-
1428
+ node tools/ci/workspace-acceptance.mjs seal
1429
+ .ebk/evidence/ci/workspace-acceptance/manifest.json
1430
+ - name: Retain exact protected-route acceptance
1431
+ if: steps.seal.outputs.sealed == 'true'
1432
+ uses: ${uploadArtifactAction}
1433
+ with:
1434
+ name: workspace-acceptance-\${{ env.EBK_CI_CANDIDATE_SHA }}-\${{ github.run_id }}-\${{ github.run_attempt }}
1435
+ path: .ebk/evidence/ci/workspace-acceptance
1436
+ if-no-files-found: error
1437
+ include-hidden-files: true
1438
+ retention-days: 30
1439
+
1440
+ release-candidate:
1441
+ name: build immutable release candidates
1442
+ needs: [acceptance, policy, verify]
1443
+ if: >-
1444
+ always() &&
1445
+ github.event_name == 'push' &&
1446
+ needs.acceptance.outputs.history_only != 'true' &&
1447
+ (needs.acceptance.outputs.reused == 'true' ||
1448
+ (needs.policy.result == 'success' && needs.verify.result == 'success'))${releaseCandidateBranchCondition}
1449
+ runs-on: ubuntu-latest
1450
+ steps:
1451
+ - uses: ${checkoutAction}
1452
+ with:
1453
+ fetch-depth: 0
1454
+ ref: \${{ github.sha }}
1455
+ - uses: ${pnpmAction}
1456
+ - uses: ${setupNodeAction}
1457
+ with:
1458
+ node-version: ${tool_versions_js_1.WORKSPACE_NODE_VERSION}
1459
+ cache: pnpm
1460
+ - run: node tools/ci/install.mjs
1461
+ ${nxCacheSteps}
1114
1462
  - name: Build immutable release candidates
1115
- if: github.event_name == 'push'
1463
+ id: candidates
1116
1464
  env:
1117
1465
  EBK_SOURCE_COMMIT: \${{ github.sha }}
1118
1466
  run: |
1119
- slice_projects="$(pnpm nx show projects --with-target=release-candidate --json | jq -r 'join(",")')"
1120
- test -n "$slice_projects" ||
1121
- { echo 'At least one release-candidate target is required'; exit 1; }
1122
- pnpm nx run-many -t release-candidate --projects="$slice_projects" --parallel=1
1467
+ slice_projects="$(node tools/ci/release-candidates.mjs)"
1468
+ if [ -z "$slice_projects" ]; then
1469
+ echo 'No application release producers; application artifacts are not applicable.'
1470
+ echo 'available=false' >> "$GITHUB_OUTPUT"
1471
+ exit 0
1472
+ fi
1473
+ pnpm nx run-many -t release-candidate --projects="$slice_projects" --parallel=3
1123
1474
  mkdir -p .ebk/evidence/release-candidate/packages
1124
1475
  IFS=',' read -r -a projects <<< "$slice_projects"
1125
1476
  for project in "\${projects[@]}"; do
@@ -1129,8 +1480,9 @@ jobs:
1129
1480
  cp packages/"$project"/.ebk/evidence/release-candidate/*.json \
1130
1481
  ".ebk/evidence/release-candidate/packages/$project/"
1131
1482
  done
1483
+ echo 'available=true' >> "$GITHUB_OUTPUT"
1132
1484
  - name: Retain exact immutable release candidate
1133
- if: github.event_name == 'push'
1485
+ if: steps.candidates.outputs.available == 'true'
1134
1486
  uses: ${uploadArtifactAction}
1135
1487
  with:
1136
1488
  name: release-candidate-\${{ github.sha }}
@@ -1138,6 +1490,7 @@ jobs:
1138
1490
  include-hidden-files: true
1139
1491
  retention-days: 30
1140
1492
  path: .ebk/evidence/release-candidate
1493
+ ${(0, workspace_files_thin_deploy_js_1.renderThinDeployVerifyCaller)(deploymentProfile, deliveryLane, productionApprovalMode)}
1141
1494
  `,
1142
1495
  '.github/workflows/deploy.yml': `name: Deploy
1143
1496
 
@@ -1165,7 +1518,97 @@ permissions:
1165
1518
  contents: read
1166
1519
 
1167
1520
  jobs:
1521
+ history-classification:
1522
+ name: classify protected history-only sync
1523
+ if: github.event_name == 'push' && github.ref == 'refs/heads/develop'
1524
+ runs-on: ubuntu-latest
1525
+ permissions:
1526
+ actions: read
1527
+ contents: read
1528
+ pull-requests: read
1529
+ outputs:
1530
+ history_only: \${{ steps.resolve.outputs.history_only == 'true' && steps.restore.outcome == 'success' && steps.verify.outcome == 'success' }}
1531
+ steps:
1532
+ - name: Check out immutable protected candidate
1533
+ uses: ${checkoutAction}
1534
+ with:
1535
+ fetch-depth: 0
1536
+ ref: \${{ github.sha }}
1537
+ - uses: ${setupNodeAction}
1538
+ with:
1539
+ node-version: ${tool_versions_js_1.WORKSPACE_NODE_VERSION}
1540
+ - name: Resolve exact protected-route acceptance
1541
+ id: resolve
1542
+ env:
1543
+ GH_TOKEN: \${{ github.token }}
1544
+ run: node tools/ci/workspace-acceptance.mjs resolve
1545
+ - name: Restore exact protected-route acceptance
1546
+ if: steps.resolve.outputs.reused == 'true'
1547
+ id: restore
1548
+ continue-on-error: true
1549
+ env:
1550
+ EBK_ACCEPTANCE_ARTIFACT_DIGEST: \${{ steps.resolve.outputs.artifact_digest }}
1551
+ EBK_ACCEPTANCE_ARTIFACT_ID: \${{ steps.resolve.outputs.artifact_id }}
1552
+ EBK_ACCEPTANCE_ARTIFACT_SIZE: \${{ steps.resolve.outputs.artifact_size }}
1553
+ GH_TOKEN: \${{ github.token }}
1554
+ run: >-
1555
+ node tools/ci/workspace-acceptance.mjs download
1556
+ .ebk/evidence/reused-history-acceptance
1557
+ "$EBK_ACCEPTANCE_ARTIFACT_ID"
1558
+ "$EBK_ACCEPTANCE_ARTIFACT_DIGEST"
1559
+ "$EBK_ACCEPTANCE_ARTIFACT_SIZE"
1560
+ - name: Verify exact history-only acceptance
1561
+ if: steps.resolve.outputs.reused == 'true' && steps.restore.outcome == 'success'
1562
+ id: verify
1563
+ continue-on-error: true
1564
+ env:
1565
+ EBK_ACCEPTANCE_BASE: \${{ steps.resolve.outputs.base_commit }}
1566
+ EBK_ACCEPTANCE_HEAD: \${{ steps.resolve.outputs.head_commit }}
1567
+ EBK_ACCEPTANCE_ROUTE: \${{ steps.resolve.outputs.route }}
1568
+ EBK_ACCEPTANCE_RUN_ATTEMPT: \${{ steps.resolve.outputs.run_attempt }}
1569
+ EBK_ACCEPTANCE_RUN_ID: \${{ steps.resolve.outputs.run_id }}
1570
+ run: >-
1571
+ node tools/ci/workspace-acceptance.mjs verify
1572
+ .ebk/evidence/reused-history-acceptance/manifest.json
1573
+ "$EBK_ACCEPTANCE_HEAD"
1574
+ "$EBK_ACCEPTANCE_BASE"
1575
+ "$EBK_ACCEPTANCE_ROUTE"
1576
+ "$EBK_ACCEPTANCE_RUN_ID"
1577
+ "$EBK_ACCEPTANCE_RUN_ATTEMPT"
1578
+
1579
+ deployment-mode:
1580
+ name: classify deployment profile
1581
+ runs-on: ubuntu-latest
1582
+ outputs:
1583
+ profile: \${{ steps.profile.outputs.profile }}
1584
+ steps:
1585
+ - name: Check out immutable deployment policy
1586
+ uses: ${checkoutAction}
1587
+ with:
1588
+ ref: \${{ github.sha }}
1589
+ - id: profile
1590
+ name: Read exact committed deployment profile
1591
+ run: |
1592
+ node - <<'NODE'
1593
+ const { readFileSync } = require('node:fs');
1594
+ const policy = JSON.parse(readFileSync('ebk.repository-policy.json', 'utf8'));
1595
+ const profiles = new Set(['starter', 'production-isolated', 'fully-isolated']);
1596
+ if (!profiles.has(policy.deploymentProfile)) {
1597
+ throw new Error('Committed deployment profile is absent or invalid.');
1598
+ }
1599
+ require('node:fs').appendFileSync(
1600
+ process.env.GITHUB_OUTPUT,
1601
+ 'profile=' + policy.deploymentProfile + '\\n',
1602
+ );
1603
+ NODE
1604
+
1168
1605
  route:
1606
+ name: ${protectedDeploymentRouteJobName}
1607
+ needs: [history-classification, deployment-mode]
1608
+ if: >-
1609
+ always() &&
1610
+ needs.history-classification.outputs.history_only != 'true' &&
1611
+ needs.deployment-mode.result == 'success'
1169
1612
  runs-on: ubuntu-latest
1170
1613
  steps:
1171
1614
  - name: Validate protected ref
@@ -1178,14 +1621,25 @@ jobs:
1178
1621
  esac
1179
1622
 
1180
1623
  plan-staging:
1181
- needs: route
1182
- if: github.ref == 'refs/heads/develop'
1624
+ needs: [route, deployment-mode]
1625
+ if: >-
1626
+ always() &&
1627
+ needs.route.result == 'success' &&
1628
+ needs.deployment-mode.result == 'success' &&
1629
+ github.ref == 'refs/heads/develop' &&
1630
+ (github.event_name == 'workflow_dispatch' ||
1631
+ needs.deployment-mode.outputs.profile != 'starter')
1183
1632
  runs-on: ubuntu-latest
1184
1633
  outputs:
1634
+ deployment_profile: \${{ steps.plan.outputs.deployment_profile }}
1635
+ deployment_region: \${{ steps.plan.outputs.deployment_region }}
1636
+ development_account_id: \${{ steps.plan.outputs.development_account_id }}
1185
1637
  foundation_project: \${{ steps.plan.outputs.foundation_project }}
1186
1638
  has_slices: \${{ steps.plan.outputs.has_slices }}
1187
1639
  ordered_projects: \${{ steps.plan.outputs.ordered_projects }}
1640
+ production_account_id: \${{ steps.plan.outputs.production_account_id }}
1188
1641
  slice_projects: \${{ steps.plan.outputs.slice_projects }}
1642
+ staging_account_id: \${{ steps.plan.outputs.staging_account_id }}
1189
1643
  steps:
1190
1644
  - uses: ${checkoutAction}
1191
1645
  with:
@@ -1208,7 +1662,7 @@ jobs:
1208
1662
  release-source-staging:
1209
1663
  name: select and seal staging release source
1210
1664
  needs: plan-staging
1211
- if: \${{ needs.plan-staging.result == 'success' && (github.event_name == 'push' && 'deploy' || inputs.action) != 'unlock' }}
1665
+ if: \${{ always() && needs.plan-staging.result == 'success' && (github.event_name == 'push' && 'deploy' || inputs.action) != 'unlock' }}
1212
1666
  runs-on: ubuntu-latest
1213
1667
  permissions:
1214
1668
  actions: read
@@ -1396,16 +1850,25 @@ jobs:
1396
1850
  path: .ebk/evidence/staging-release-source/**
1397
1851
 
1398
1852
  plan-production:
1399
- needs: route
1400
- if: github.ref == 'refs/heads/main'
1853
+ needs: [route, deployment-mode]
1854
+ if: >-
1855
+ always() &&
1856
+ needs.route.result == 'success' &&
1857
+ needs.deployment-mode.result == 'success' &&
1858
+ github.ref == 'refs/heads/main'
1401
1859
  runs-on: ubuntu-latest
1402
1860
  outputs:
1861
+ deployment_profile: \${{ steps.plan.outputs.deployment_profile }}
1862
+ deployment_region: \${{ steps.plan.outputs.deployment_region }}
1863
+ development_account_id: \${{ steps.plan.outputs.development_account_id }}
1403
1864
  foundation_project: \${{ steps.plan.outputs.foundation_project }}
1404
1865
  foundation_profile: \${{ steps.plan.outputs.foundation_profile }}
1405
1866
  has_slices: \${{ steps.plan.outputs.has_slices }}
1406
1867
  ordered_projects: \${{ steps.plan.outputs.ordered_projects }}
1407
1868
  production_approval_mode: \${{ steps.plan.outputs.production_approval_mode }}
1869
+ production_account_id: \${{ steps.plan.outputs.production_account_id }}
1408
1870
  slice_projects: \${{ steps.plan.outputs.slice_projects }}
1871
+ staging_account_id: \${{ steps.plan.outputs.staging_account_id }}
1409
1872
  steps:
1410
1873
  - uses: ${checkoutAction}
1411
1874
  with:
@@ -1699,7 +2162,7 @@ jobs:
1699
2162
  container-platform-staging:
1700
2163
  name: native staging OCI \${{ matrix.platform }}
1701
2164
  needs: [plan-staging, release-source-staging]
1702
- if: \${{ (github.event_name == 'push' && 'deploy' || inputs.action) == 'deploy' && needs.plan-staging.outputs.has_slices == 'true' && needs.release-source-staging.result == 'success' }}
2165
+ if: \${{ always() && (github.event_name == 'push' && 'deploy' || inputs.action) == 'deploy' && needs.plan-staging.result == 'success' && needs.plan-staging.outputs.slice_projects != '' && needs.release-source-staging.result == 'success' }}
1703
2166
  strategy:
1704
2167
  fail-fast: false
1705
2168
  matrix:
@@ -1726,6 +2189,8 @@ jobs:
1726
2189
  EBK_RELEASE_SOURCE_COMMIT: \${{ needs.release-source-staging.outputs.source_commit }}
1727
2190
  EBK_SOURCE_COMMIT: \${{ needs.release-source-staging.outputs.source_commit }}
1728
2191
  EBK_STAGE: staging
2192
+ EBK_POLICY_ACCOUNT_ID: \${{ needs.plan-staging.outputs.staging_account_id }}
2193
+ EBK_POLICY_REGION: \${{ needs.plan-staging.outputs.deployment_region }}
1729
2194
  SLICE_PROJECTS: \${{ needs.plan-staging.outputs.slice_projects }}
1730
2195
  steps:
1731
2196
  - uses: ${checkoutAction}
@@ -1741,6 +2206,10 @@ jobs:
1741
2206
  - run: pnpm install --frozen-lockfile --ignore-scripts --ignore-pnpmfile
1742
2207
  - name: Verify managed workspace integrity before AWS authority
1743
2208
  run: pnpm nx run ${name}:workspace-health
2209
+ - name: Bind staging account to committed deployment topology
2210
+ run: |
2211
+ test "$AWS_EXPECTED_ACCOUNT_ID" = "$EBK_POLICY_ACCOUNT_ID"
2212
+ test "$AWS_REGION" = "$EBK_POLICY_REGION"
1744
2213
  ${stagingReleaseCandidateDownloadSteps(downloadArtifactAction)} - uses: ${configureAwsAction}
1745
2214
  with:
1746
2215
  role-to-assume: \${{ secrets.AWS_DEPLOY_ROLE_ARN }}
@@ -1780,6 +2249,8 @@ ${stagingReleaseCandidateDownloadSteps(downloadArtifactAction)} - uses: ${c
1780
2249
  EBK_RELEASE_SOURCE_COMMIT: \${{ needs.release-source-staging.outputs.source_commit }}
1781
2250
  EBK_SOURCE_COMMIT: \${{ needs.release-source-staging.outputs.source_commit }}
1782
2251
  EBK_STAGE: staging
2252
+ EBK_POLICY_ACCOUNT_ID: \${{ needs.plan-staging.outputs.staging_account_id }}
2253
+ EBK_POLICY_REGION: \${{ needs.plan-staging.outputs.deployment_region }}
1783
2254
  SLICE_PROJECTS: \${{ needs.plan-staging.outputs.slice_projects }}
1784
2255
  steps:
1785
2256
  - uses: ${checkoutAction}
@@ -1795,6 +2266,10 @@ ${stagingReleaseCandidateDownloadSteps(downloadArtifactAction)} - uses: ${c
1795
2266
  - run: pnpm install --frozen-lockfile --ignore-scripts --ignore-pnpmfile
1796
2267
  - name: Verify managed workspace integrity before AWS authority
1797
2268
  run: pnpm nx run ${name}:workspace-health
2269
+ - name: Bind staging account to committed deployment topology
2270
+ run: |
2271
+ test "$AWS_EXPECTED_ACCOUNT_ID" = "$EBK_POLICY_ACCOUNT_ID"
2272
+ test "$AWS_REGION" = "$EBK_POLICY_REGION"
1798
2273
  ${stagingReleaseCandidateDownloadSteps(downloadArtifactAction)} - name: Restore both native platform records
1799
2274
  uses: ${downloadArtifactAction}
1800
2275
  with:
@@ -1825,7 +2300,7 @@ ${stagingReleaseCandidateDownloadSteps(downloadArtifactAction)} - name: Res
1825
2300
  container-promote-production:
1826
2301
  name: promote reviewed OCI indexes to production
1827
2302
  needs: [plan-production, release-source-production]
1828
- if: \${{ always() && needs.plan-production.result == 'success' && needs.release-source-production.result == 'success' && needs.plan-production.outputs.has_slices == 'true' }}
2303
+ if: \${{ always() && needs.plan-production.result == 'success' && needs.release-source-production.result == 'success' && needs.plan-production.outputs.slice_projects != '' }}
1829
2304
  runs-on: ubuntu-latest
1830
2305
  environment: production
1831
2306
  permissions:
@@ -1836,6 +2311,10 @@ ${stagingReleaseCandidateDownloadSteps(downloadArtifactAction)} - name: Res
1836
2311
  AWS_DEPLOY_ROLE_ARN: \${{ secrets.AWS_DEPLOY_ROLE_ARN }}
1837
2312
  AWS_EXPECTED_ACCOUNT_ID: \${{ vars.AWS_EXPECTED_ACCOUNT_ID }}
1838
2313
  AWS_REGION: \${{ vars.AWS_REGION }}
2314
+ EBK_DEPLOYMENT_PROFILE: \${{ needs.plan-production.outputs.deployment_profile }}
2315
+ EBK_POLICY_PRODUCTION_ACCOUNT_ID: \${{ needs.plan-production.outputs.production_account_id }}
2316
+ EBK_POLICY_REGION: \${{ needs.plan-production.outputs.deployment_region }}
2317
+ EBK_POLICY_STAGING_ACCOUNT_ID: \${{ needs.plan-production.outputs.staging_account_id }}
1839
2318
  EBK_CANDIDATE_RUN_ATTEMPT: \${{ needs.release-source-production.outputs.candidate_run_attempt }}
1840
2319
  EBK_CANDIDATE_RUN_ID: \${{ needs.release-source-production.outputs.candidate_run_id }}
1841
2320
  EBK_RELEASE_SOURCE_COMMIT: \${{ needs.release-source-production.outputs.source_commit }}
@@ -1936,11 +2415,13 @@ ${stagingReleaseCandidateDownloadSteps(downloadArtifactAction)} - name: Res
1936
2415
  cp "$source_sidecar" "$target"
1937
2416
  done
1938
2417
  - uses: ${configureAwsAction}
2418
+ if: env.EBK_DEPLOYMENT_PROFILE != 'starter'
1939
2419
  with:
1940
2420
  role-to-assume: \${{ secrets.AWS_STAGING_PROMOTION_PULL_ROLE_ARN }}
1941
2421
  role-session-name: ebk-staging-container-pull-\${{ github.run_id }}-\${{ github.run_attempt }}
1942
2422
  aws-region: \${{ vars.AWS_STAGING_PROMOTION_REGION }}
1943
2423
  - name: Authenticate exact staging source registry
2424
+ if: env.EBK_DEPLOYMENT_PROFILE != 'starter'
1944
2425
  env:
1945
2426
  EBK_STAGING_ACCOUNT_ID: \${{ vars.AWS_STAGING_PROMOTION_EXPECTED_ACCOUNT_ID }}
1946
2427
  EBK_STAGING_REGION: \${{ vars.AWS_STAGING_PROMOTION_REGION }}
@@ -1959,10 +2440,27 @@ ${stagingReleaseCandidateDownloadSteps(downloadArtifactAction)} - name: Res
1959
2440
  role-to-assume: \${{ secrets.AWS_DEPLOY_ROLE_ARN }}
1960
2441
  role-session-name: ebk-production-container-push-\${{ github.run_id }}-\${{ github.run_attempt }}
1961
2442
  aws-region: \${{ vars.AWS_REGION }}
2443
+ - name: Bind production authority to committed deployment topology
2444
+ run: |
2445
+ test "$AWS_EXPECTED_ACCOUNT_ID" = "$EBK_POLICY_PRODUCTION_ACCOUNT_ID" ||
2446
+ { echo 'Production environment account differs from committed deployment topology'; exit 1; }
2447
+ test "$AWS_REGION" = "$EBK_POLICY_REGION" ||
2448
+ { echo 'Production environment Region differs from committed deployment topology'; exit 1; }
2449
+ - name: Authenticate shared-account staging source registry
2450
+ if: env.EBK_DEPLOYMENT_PROFILE == 'starter'
2451
+ run: |
2452
+ test "$EBK_POLICY_STAGING_ACCOUNT_ID" = "$EBK_POLICY_PRODUCTION_ACCOUNT_ID" ||
2453
+ { echo 'Starter promotion requires one shared AWS account'; exit 1; }
2454
+ registry_suffix=.com
2455
+ case "$EBK_POLICY_REGION" in cn-*) registry_suffix=.com.cn ;; esac
2456
+ aws ecr get-login-password --region "$EBK_POLICY_REGION" |
2457
+ docker login --username AWS --password-stdin \
2458
+ "$EBK_POLICY_STAGING_ACCOUNT_ID.dkr.ecr.$EBK_POLICY_REGION.amazonaws$registry_suffix"
1962
2459
  - name: Promote exact staging indexes without rebuilding
1963
2460
  env:
1964
- EBK_STAGING_PROMOTION_EXPECTED_ACCOUNT_ID: \${{ vars.AWS_STAGING_PROMOTION_EXPECTED_ACCOUNT_ID }}
1965
- EBK_STAGING_PROMOTION_REGION: \${{ vars.AWS_STAGING_PROMOTION_REGION }}
2461
+ EBK_DEPLOYMENT_PROFILE: \${{ needs.plan-production.outputs.deployment_profile }}
2462
+ EBK_STAGING_PROMOTION_EXPECTED_ACCOUNT_ID: \${{ needs.plan-production.outputs.deployment_profile == 'starter' && needs.plan-production.outputs.staging_account_id || vars.AWS_STAGING_PROMOTION_EXPECTED_ACCOUNT_ID }}
2463
+ EBK_STAGING_PROMOTION_REGION: \${{ needs.plan-production.outputs.deployment_profile == 'starter' && needs.plan-production.outputs.deployment_region || vars.AWS_STAGING_PROMOTION_REGION }}
1966
2464
  run: |
1967
2465
  test "$(aws sts get-caller-identity --query Account --output text)" = "$AWS_EXPECTED_ACCOUNT_ID" ||
1968
2466
  { echo 'Production promotion role account differs from policy'; exit 1; }
@@ -1995,7 +2493,7 @@ ${planRoleAuditJob('production', configureAwsAction)}${planRoleAuditJob('staging
1995
2493
  (
1996
2494
  needs.release-source-production.result == 'success' &&
1997
2495
  (
1998
- needs.plan-production.outputs.has_slices != 'true' ||
2496
+ needs.plan-production.outputs.slice_projects == '' ||
1999
2497
  needs.container-promote-production.result == 'success'
2000
2498
  )
2001
2499
  )
@@ -2089,7 +2587,7 @@ ${infrastructureCandidateDownloadSteps(downloadArtifactAction, {
2089
2587
  runId: '${{ needs.release-source-production.outputs.candidate_run_id }}',
2090
2588
  verifiedStagingRunId: '${{ needs.release-source-production.outputs.verified_staging_run_id }}',
2091
2589
  })} - name: Restore exact production container bindings
2092
- if: env.EBK_ACTION == 'deploy' && needs.plan-production.outputs.has_slices == 'true'
2590
+ if: env.EBK_ACTION == 'deploy' && needs.plan-production.outputs.slice_projects != ''
2093
2591
  env:
2094
2592
  ACTIONS_ID_TOKEN_REQUEST_TOKEN: ''
2095
2593
  ACTIONS_ID_TOKEN_REQUEST_URL: ''
@@ -2150,7 +2648,7 @@ ${planRoleAuditStep('main', 'verify')}
2150
2648
  (github.event_name == 'push' && 'deploy' || inputs.action) != 'unlock' &&
2151
2649
  (
2152
2650
  (github.event_name == 'push' && 'deploy' || inputs.action) != 'deploy' ||
2153
- needs.plan-staging.outputs.has_slices != 'true' ||
2651
+ needs.plan-staging.outputs.slice_projects == '' ||
2154
2652
  needs.container-assemble-staging.result == 'success'
2155
2653
  )
2156
2654
  runs-on: ubuntu-latest
@@ -2181,6 +2679,8 @@ ${planRoleAuditStep('main', 'verify')}
2181
2679
  EBK_RELEASE_SOURCE_COMMIT: \${{ needs.release-source-staging.outputs.source_commit }}
2182
2680
  EBK_SOURCE_COMMIT: \${{ github.sha }}
2183
2681
  EBK_STAGE: staging
2682
+ EBK_POLICY_ACCOUNT_ID: \${{ needs.plan-staging.outputs.staging_account_id }}
2683
+ EBK_POLICY_REGION: \${{ needs.plan-staging.outputs.deployment_region }}
2184
2684
  FOUNDATION_PROJECT: \${{ needs.plan-staging.outputs.foundation_project }}
2185
2685
  ORDERED_PROJECTS: \${{ needs.plan-staging.outputs.ordered_projects }}
2186
2686
  SLICE_PROJECTS: \${{ needs.plan-staging.outputs.slice_projects }}
@@ -2238,7 +2738,7 @@ ${planApplicationBindingCheck()} case "$AWS_PLAN_ROLE_ARN" in
2238
2738
  ACTIONS_ID_TOKEN_REQUEST_URL: ''
2239
2739
  run: pnpm nx run ${name}:workspace-health
2240
2740
  ${stagingReleaseCandidateDownloadSteps(downloadArtifactAction)} - name: Restore exact staging container bindings
2241
- if: env.EBK_ACTION == 'deploy' && needs.plan-staging.outputs.has_slices == 'true'
2741
+ if: env.EBK_ACTION == 'deploy' && needs.plan-staging.outputs.slice_projects != ''
2242
2742
  env:
2243
2743
  ACTIONS_ID_TOKEN_REQUEST_TOKEN: ''
2244
2744
  ACTIONS_ID_TOKEN_REQUEST_URL: ''
@@ -2288,14 +2788,14 @@ ${planRoleAuditStep('develop', 'verify')}
2288
2788
  packages/*/.ebk/evidence/infrastructure-plan-command.json
2289
2789
 
2290
2790
  deploy-staging:
2291
- name: apply staging plan \${{ needs.infrastructure-plan-staging.outputs.plan_digest || 'recovery-without-resource-plan' }}
2791
+ name: ${stagingApplyJobPrefix}\${{ needs.infrastructure-plan-staging.outputs.plan_digest || 'recovery-without-resource-plan' }}
2292
2792
  needs: [plan-staging, release-source-staging, container-assemble-staging, infrastructure-plan-staging]
2293
2793
  if: |
2294
2794
  always() &&
2295
2795
  needs.plan-staging.result == 'success' &&
2296
2796
  (
2297
2797
  (github.event_name == 'push' && 'deploy' || inputs.action) != 'deploy' ||
2298
- needs.plan-staging.outputs.has_slices != 'true' ||
2798
+ needs.plan-staging.outputs.slice_projects == '' ||
2299
2799
  needs.container-assemble-staging.result == 'success'
2300
2800
  ) &&
2301
2801
  (
@@ -2357,7 +2857,7 @@ ${stagingReleaseCandidateDownloadSteps(downloadArtifactAction)} - name: Res
2357
2857
  name: infrastructure-plan-staging-\${{ github.run_id }}-\${{ github.run_attempt }}
2358
2858
  path: .
2359
2859
  - name: Restore exact staging container bindings
2360
- if: env.EBK_ACTION == 'deploy' && needs.plan-staging.outputs.has_slices == 'true'
2860
+ if: env.EBK_ACTION == 'deploy' && needs.plan-staging.outputs.slice_projects != ''
2361
2861
  uses: ${downloadArtifactAction}
2362
2862
  with:
2363
2863
  name: container-release-staging-\${{ github.run_id }}-\${{ github.run_attempt }}
@@ -2367,6 +2867,10 @@ ${stagingReleaseCandidateDownloadSteps(downloadArtifactAction)} - name: Res
2367
2867
  test -n "$AWS_DEPLOY_ROLE_ARN" || { echo 'staging AWS_DEPLOY_ROLE_ARN is required'; exit 1; }
2368
2868
  test -n "$AWS_EXPECTED_ACCOUNT_ID" || { echo 'staging AWS_EXPECTED_ACCOUNT_ID is required'; exit 1; }
2369
2869
  test -n "$AWS_REGION" || { echo 'staging AWS_REGION is required'; exit 1; }
2870
+ test "$AWS_EXPECTED_ACCOUNT_ID" = "$EBK_POLICY_ACCOUNT_ID" ||
2871
+ { echo 'staging environment account differs from committed deployment topology'; exit 1; }
2872
+ test "$AWS_REGION" = "$EBK_POLICY_REGION" ||
2873
+ { echo 'staging environment Region differs from committed deployment topology'; exit 1; }
2370
2874
  test -n "$EBK_FLEET_ORIGIN" ||
2371
2875
  { echo 'staging environment EBK_FLEET_ORIGIN is required'; exit 1; }
2372
2876
  test "$EBK_FLEET_ORIGIN" = "$EBK_PLANNED_FLEET_ORIGIN" ||
@@ -2378,8 +2882,7 @@ ${stagingReleaseCandidateDownloadSteps(downloadArtifactAction)} - name: Res
2378
2882
  test -n "$AWS_PLAN_GENERATED_SECRET_ARNS_JSON" &&
2379
2883
  test -n "$AWS_PLAN_PROVIDER_REFRESH_POLICY_JSON" ||
2380
2884
  { echo 'staging exact plan-discovery inputs are required'; exit 1; }
2381
- test -n "$AWS_PLAN_ROLE_ARN" &&
2382
- test -n "$AWS_PLAN_STATE_KMS_KEY_ARN" ||
2885
+ test -n "$AWS_PLAN_ROLE_ARN" ||
2383
2886
  { echo 'staging exact plan-authority inputs are required'; exit 1; }
2384
2887
  case "$AWS_DEPLOY_ROLE_ARN" in
2385
2888
  arn:aws:iam::$AWS_EXPECTED_ACCOUNT_ID:role/*) ;;
@@ -2946,7 +3449,7 @@ ${stagingReleaseCandidateDownloadSteps(downloadArtifactAction)} - name: Res
2946
3449
  needs.plan-production.result == 'success' &&
2947
3450
  (
2948
3451
  (github.event_name == 'push' && 'deploy' || inputs.action) != 'deploy' ||
2949
- needs.plan-production.outputs.has_slices != 'true' ||
3452
+ needs.plan-production.outputs.slice_projects == '' ||
2950
3453
  needs.container-promote-production.result == 'success'
2951
3454
  ) &&
2952
3455
  (
@@ -3003,6 +3506,8 @@ ${stagingReleaseCandidateDownloadSteps(downloadArtifactAction)} - name: Res
3003
3506
  EBK_APPROVED_MAIN_COMMIT: \${{ github.sha }}
3004
3507
  EBK_SOURCE_COMMIT: \${{ github.sha }}
3005
3508
  EBK_STAGE: production
3509
+ EBK_POLICY_ACCOUNT_ID: \${{ needs.plan-production.outputs.production_account_id }}
3510
+ EBK_POLICY_REGION: \${{ needs.plan-production.outputs.deployment_region }}
3006
3511
  EBK_VERIFIED_STAGING_RUN_ID: \${{ needs.release-source-production.outputs.verified_staging_run_id }}
3007
3512
  FOUNDATION_PROJECT: \${{ needs.plan-production.outputs.foundation_project }}
3008
3513
  ORDERED_PROJECTS: \${{ needs.plan-production.outputs.ordered_projects }}
@@ -3089,7 +3594,7 @@ ${stagingReleaseCandidateDownloadSteps(downloadArtifactAction)} - name: Res
3089
3594
  name: infrastructure-plan-production-\${{ github.run_id }}-\${{ github.run_attempt }}
3090
3595
  path: .
3091
3596
  - name: Restore exact production container bindings
3092
- if: env.EBK_ACTION == 'deploy' && needs.plan-production.outputs.has_slices == 'true'
3597
+ if: env.EBK_ACTION == 'deploy' && needs.plan-production.outputs.slice_projects != ''
3093
3598
  uses: ${downloadArtifactAction}
3094
3599
  with:
3095
3600
  name: container-release-production-\${{ github.run_id }}-\${{ github.run_attempt }}
@@ -3099,6 +3604,10 @@ ${stagingReleaseCandidateDownloadSteps(downloadArtifactAction)} - name: Res
3099
3604
  test -n "$AWS_DEPLOY_ROLE_ARN" || { echo 'production AWS_DEPLOY_ROLE_ARN is required'; exit 1; }
3100
3605
  test -n "$AWS_EXPECTED_ACCOUNT_ID" || { echo 'production AWS_EXPECTED_ACCOUNT_ID is required'; exit 1; }
3101
3606
  test -n "$AWS_REGION" || { echo 'production AWS_REGION is required'; exit 1; }
3607
+ test "$AWS_EXPECTED_ACCOUNT_ID" = "$EBK_POLICY_ACCOUNT_ID" ||
3608
+ { echo 'production environment account differs from committed deployment topology'; exit 1; }
3609
+ test "$AWS_REGION" = "$EBK_POLICY_REGION" ||
3610
+ { echo 'production environment Region differs from committed deployment topology'; exit 1; }
3102
3611
  test -n "$EBK_FLEET_ORIGIN" ||
3103
3612
  { echo 'production environment EBK_FLEET_ORIGIN is required'; exit 1; }
3104
3613
  test "$EBK_FLEET_ORIGIN" = "$EBK_PLANNED_FLEET_ORIGIN" ||
@@ -3110,8 +3619,7 @@ ${stagingReleaseCandidateDownloadSteps(downloadArtifactAction)} - name: Res
3110
3619
  test -n "$AWS_PLAN_GENERATED_SECRET_ARNS_JSON" &&
3111
3620
  test -n "$AWS_PLAN_PROVIDER_REFRESH_POLICY_JSON" ||
3112
3621
  { echo 'production exact plan-discovery inputs are required'; exit 1; }
3113
- test -n "$AWS_PLAN_ROLE_ARN" &&
3114
- test -n "$AWS_PLAN_STATE_KMS_KEY_ARN" ||
3622
+ test -n "$AWS_PLAN_ROLE_ARN" ||
3115
3623
  { echo 'production exact plan-authority inputs are required'; exit 1; }
3116
3624
  case "$AWS_DEPLOY_ROLE_ARN" in
3117
3625
  arn:aws:iam::$AWS_EXPECTED_ACCOUNT_ID:role/*) ;;
@@ -3492,7 +4000,7 @@ on:
3492
4000
  description: Exact protected deployment stage
3493
4001
  required: true
3494
4002
  type: choice
3495
- options: [staging, production]
4003
+ options: [${ecrInspectorWorkflowStageChoices}]
3496
4004
  deployment_commit:
3497
4005
  description: Exact successful protected deployment commit
3498
4006
  required: true
@@ -3501,8 +4009,8 @@ on:
3501
4009
  description: Exact successful Deploy workflow run ID
3502
4010
  required: true
3503
4011
  type: string
3504
- release_artifact_name:
3505
- description: Exact release-evidence artifact from that run
4012
+ deployment_receipt_artifact_name:
4013
+ description: Exact deployment-receipt artifact from that run
3506
4014
  required: true
3507
4015
  type: string
3508
4016
  slice:
@@ -3556,7 +4064,7 @@ jobs:
3556
4064
  EBK_DEPLOYMENT_ARN: \${{ inputs.deployment_arn }}
3557
4065
  EBK_DEPLOYMENT_KIND: \${{ inputs.deployment_kind }}
3558
4066
  EBK_DEPLOY_RUN_ID: \${{ inputs.deploy_run_id }}
3559
- EBK_RELEASE_ARTIFACT_NAME: \${{ inputs.release_artifact_name }}
4067
+ EBK_DEPLOYMENT_RECEIPT_ARTIFACT_NAME: \${{ inputs.deployment_receipt_artifact_name }}
3560
4068
  EBK_SLICE: \${{ inputs.slice }}
3561
4069
  EBK_DEPLOYMENT_COMMIT: \${{ inputs.deployment_commit }}
3562
4070
  EBK_STAGE: \${{ inputs.stage }}
@@ -3579,10 +4087,10 @@ jobs:
3579
4087
  esac
3580
4088
  expected_branch=develop
3581
4089
  if [ "$EBK_STAGE" = production ]; then expected_branch=main; fi
3582
- expected_artifact="release-evidence-$EBK_STAGE-$EBK_DEPLOY_RUN_ID-"
3583
- case "$EBK_RELEASE_ARTIFACT_NAME" in
3584
- "$expected_artifact"*) attempt="\${EBK_RELEASE_ARTIFACT_NAME#"$expected_artifact"}" ;;
3585
- *) echo 'Release evidence artifact does not bind the exact deploy run'; exit 1 ;;
4090
+ expected_artifact="deployment-receipt-$EBK_STAGE-$EBK_DEPLOY_RUN_ID-"
4091
+ case "$EBK_DEPLOYMENT_RECEIPT_ARTIFACT_NAME" in
4092
+ "$expected_artifact"*) attempt="\${EBK_DEPLOYMENT_RECEIPT_ARTIFACT_NAME#"$expected_artifact"}" ;;
4093
+ *) echo 'Deployment receipt artifact does not bind the exact deploy run'; exit 1 ;;
3586
4094
  esac
3587
4095
  case "$attempt" in
3588
4096
  ''|0*|*[!0-9]*) echo 'Release evidence artifact has an invalid run attempt'; exit 1 ;;
@@ -3595,7 +4103,7 @@ jobs:
3595
4103
  test "$(printf '%s' "$run" | jq -r .path)" = ".github/workflows/deploy.yml" ||
3596
4104
  { echo 'Deploy run is not the exact successful protected source authority'; exit 1; }
3597
4105
  artifacts="$(gh api "/repos/$GH_REPO/actions/runs/$EBK_DEPLOY_RUN_ID/artifacts?per_page=100")"
3598
- count="$(printf '%s' "$artifacts" | jq --arg name "$EBK_RELEASE_ARTIFACT_NAME" \
4106
+ count="$(printf '%s' "$artifacts" | jq --arg name "$EBK_DEPLOYMENT_RECEIPT_ARTIFACT_NAME" \
3599
4107
  '[.artifacts[] | select(.name == $name and .expired == false)] | length')"
3600
4108
  test "$count" = 1 ||
3601
4109
  { echo 'Exact unexpired release evidence artifact is absent or ambiguous'; exit 1; }
@@ -3612,7 +4120,7 @@ jobs:
3612
4120
  - uses: ${downloadArtifactAction}
3613
4121
  with:
3614
4122
  github-token: \${{ github.token }}
3615
- name: \${{ inputs.release_artifact_name }}
4123
+ name: \${{ inputs.deployment_receipt_artifact_name }}
3616
4124
  path: .
3617
4125
  run-id: \${{ inputs.deploy_run_id }}
3618
4126
  - uses: ${pnpmAction}
@@ -3719,7 +4227,7 @@ jobs:
3719
4227
  { encoding: "utf8" },
3720
4228
  ).trim()
3721
4229
  ) fail("Production deployment tree differs from its reviewed release source.");
3722
- } else if (stage !== "staging") {
4230
+ } else if (stage !== "${rehearsalStage}") {
3723
4231
  fail("Protected release stage is invalid.");
3724
4232
  }
3725
4233
  const productionKeys = [
@@ -3775,7 +4283,7 @@ jobs:
3775
4283
  !digest.test(binding.candidateDigest ?? "") ||
3776
4284
  (stage === "production" &&
3777
4285
  !digest.test(binding.lifecycleDigest ?? "")) ||
3778
- (stage === "staging" && binding.stage !== stage) ||
4286
+ (stage === "${rehearsalStage}" && binding.stage !== stage) ||
3779
4287
  binding.runId !== process.env.EBK_DEPLOY_RUN_ID ||
3780
4288
  binding.runAttempt !== process.env.EBK_DEPLOY_RUN_ATTEMPT ||
3781
4289
  binding.workflow !== expectedWorkflow
@@ -3854,7 +4362,7 @@ on:
3854
4362
  description: Protected deployed stage
3855
4363
  required: true
3856
4364
  type: choice
3857
- options: [staging, production]
4365
+ options: [${protectedWorkflowStageChoices}]
3858
4366
  project:
3859
4367
  description: Exact Product Slice project
3860
4368
  required: true
@@ -3954,7 +4462,7 @@ jobs:
3954
4462
  EBK_REPORT_RUN_ID: \${{ inputs.report_run_id }}
3955
4463
  run: |
3956
4464
  case "$EBK_STAGE:$EBK_PROTECTED_REF" in
3957
- staging:refs/heads/develop|production:refs/heads/main) ;;
4465
+ ${protectedWorkflowStageBranchCases}) ;;
3958
4466
  *) echo 'Async recovery stage does not match its protected branch'; exit 1 ;;
3959
4467
  esac
3960
4468
  case "$EBK_PROJECT" in
@@ -4765,13 +5273,78 @@ permissions:
4765
5273
  contents: read
4766
5274
 
4767
5275
  jobs:
5276
+ deployment-classification:
5277
+ name: classify exact staging deployment
5278
+ if: >-
5279
+ github.event_name == 'workflow_run' &&
5280
+ github.event.workflow_run.event == 'push' &&
5281
+ github.event.workflow_run.head_branch == 'develop' &&
5282
+ github.event.workflow_run.conclusion == 'success'
5283
+ permissions:
5284
+ actions: read
5285
+ contents: read
5286
+ outputs:
5287
+ deployed: \${{ steps.classify.outputs.deployed }}
5288
+ runs-on: ubuntu-latest
5289
+ steps:
5290
+ - name: Check out immutable deployment policy
5291
+ uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
5292
+ with:
5293
+ persist-credentials: false
5294
+ ref: \${{ github.event.workflow_run.head_sha }}
5295
+ - id: profile
5296
+ name: Read exact committed deployment profile
5297
+ run: |
5298
+ node - <<'NODE'
5299
+ const { appendFileSync, readFileSync } = require('node:fs');
5300
+ const policy = JSON.parse(
5301
+ readFileSync('ebk.repository-policy.json', 'utf8'),
5302
+ );
5303
+ const profiles = new Set([
5304
+ 'starter',
5305
+ 'production-isolated',
5306
+ 'fully-isolated',
5307
+ ]);
5308
+ if (!profiles.has(policy.deploymentProfile)) {
5309
+ throw new Error('Committed deployment profile is absent or invalid.');
5310
+ }
5311
+ appendFileSync(
5312
+ process.env.GITHUB_OUTPUT,
5313
+ 'profile=' + policy.deploymentProfile + '\\n',
5314
+ );
5315
+ NODE
5316
+ - id: classify
5317
+ name: Require one successful thin staging deployment job
5318
+ env:
5319
+ EBK_DEPLOYMENT_PROFILE: \${{ steps.profile.outputs.profile }}
5320
+ EBK_DEPLOYMENT_RUN_ID: \${{ github.event.workflow_run.id }}
5321
+ GH_TOKEN: \${{ github.token }}
5322
+ GH_REPO: \${{ github.repository }}
5323
+ run: |
5324
+ if [ "$EBK_DEPLOYMENT_PROFILE" = starter ]; then
5325
+ echo 'deployed=false' >> "$GITHUB_OUTPUT"
5326
+ exit 0
5327
+ fi
5328
+ jobs="$(gh api \
5329
+ "/repos/$GH_REPO/actions/runs/$EBK_DEPLOYMENT_RUN_ID/jobs?filter=latest&per_page=100")"
5330
+ execute_count="$(printf '%s' "$jobs" | jq \
5331
+ '[.jobs[] | select(.name == "native deploy staging" and .conclusion == "success")] | length')"
5332
+ [ "$execute_count" = 1 ] || {
5333
+ echo 'Deploy must contain exactly one successful thin staging execution'
5334
+ exit 1
5335
+ }
5336
+ echo 'deployed=true' >> "$GITHUB_OUTPUT"
5337
+
4768
5338
  plan:
5339
+ needs: deployment-classification
4769
5340
  if: >-
4770
- (github.event_name == 'workflow_run' &&
5341
+ always() &&
5342
+ ((github.event_name == 'workflow_run' &&
4771
5343
  github.event.workflow_run.conclusion == 'success' &&
4772
5344
  github.event.workflow_run.event == 'push' &&
4773
- github.event.workflow_run.head_branch == 'develop') ||
4774
- github.event_name == 'workflow_dispatch'
5345
+ github.event.workflow_run.head_branch == 'develop' &&
5346
+ needs.deployment-classification.outputs.deployed == 'true') ||
5347
+ github.event_name == 'workflow_dispatch')
4775
5348
  outputs:
4776
5349
  deployment_run_attempt: \${{ steps.identity.outputs.deployment_run_attempt }}
4777
5350
  deployment_run_id: \${{ steps.identity.outputs.deployment_run_id }}
@@ -4878,9 +5451,10 @@ jobs:
4878
5451
  EBK_SMOKE_AUTHORITY_VERSION: \${{ vars.EBK_SMOKE_AUTHORITY_VERSION }}
4879
5452
  EBK_SMOKE_TARGETS: \${{ vars.EBK_SMOKE_TARGETS }}
4880
5453
  EBK_SOURCE_COMMIT: \${{ needs.plan.outputs.source_commit }}
4881
- PLAYWRIGHT_BROWSERS_PATH: \${{ runner.temp }}/ebk-playwright-browsers
4882
5454
  runs-on: ubuntu-latest
4883
5455
  steps:
5456
+ - name: Initialize private browser cache path
5457
+ run: echo "PLAYWRIGHT_BROWSERS_PATH=$RUNNER_TEMP/ebk-playwright-browsers" >> "$GITHUB_ENV"
4884
5458
  - name: Validate credential-free preparation
4885
5459
  run: |
4886
5460
  test -n "$AWS_EXPECTED_ACCOUNT_ID" || { echo 'AWS_EXPECTED_ACCOUNT_ID is required'; exit 1; }
@@ -5264,14 +5838,14 @@ jobs:
5264
5838
  test "$(node --version | cut -d. -f1)" = v22
5265
5839
  - name: Run exact deployed smoke
5266
5840
  working-directory: \${{ runner.temp }}/ebk-prepared-workspace
5267
- run: node node_modules/nx/bin/nx.js run "$EBK_PROJECT:smoke-deployed"
5841
+ run: node node_modules/nx/dist/bin/nx.js run "$EBK_PROJECT:smoke-deployed"
5268
5842
  - name: Verify reviewed frontend visual baseline
5269
5843
  working-directory: \${{ runner.temp }}/ebk-prepared-workspace
5270
- run: node node_modules/nx/bin/nx.js run "$EBK_PROJECT:frontend-visual"
5844
+ run: node node_modules/nx/dist/bin/nx.js run "$EBK_PROJECT:frontend-visual"
5271
5845
  - name: Verify deployed frontend security headers
5272
5846
  working-directory: \${{ runner.temp }}/ebk-prepared-workspace
5273
5847
  run: |
5274
- node node_modules/nx/bin/nx.js run \
5848
+ node node_modules/nx/dist/bin/nx.js run \
5275
5849
  "$EBK_PROJECT:frontend-security-headers" -- \
5276
5850
  --url="$EBK_SMOKE_URL"
5277
5851
  - name: Bind exact alarm and data-integrity evidence
@@ -6642,7 +7216,7 @@ on:
6642
7216
  description: Protected stage
6643
7217
  required: true
6644
7218
  type: choice
6645
- options: [staging, production]
7219
+ options: [${protectedWorkflowStageChoices}]
6646
7220
  project:
6647
7221
  description: Exact Product Slice project
6648
7222
  required: true
@@ -6668,11 +7242,9 @@ jobs:
6668
7242
  contents: read
6669
7243
  id-token: write
6670
7244
  env:
6671
- AWS_DEPLOY_ROLE_ARN: \${{ secrets.AWS_DEPLOY_ROLE_ARN }}
6672
- AWS_EXPECTED_ACCOUNT_ID: \${{ vars.AWS_EXPECTED_ACCOUNT_ID }}
6673
- AWS_REGION: \${{ vars.AWS_REGION }}
6674
7245
  EBK_ACTION: \${{ inputs.action }}
6675
7246
  EBK_PROJECT: \${{ inputs.project }}
7247
+ EBK_RENDERED_PROFILE: ${deploymentProfile}
6676
7248
  EBK_STAGE: \${{ inputs.stage }}
6677
7249
  runs-on: ubuntu-latest
6678
7250
  steps:
@@ -6681,19 +7253,12 @@ jobs:
6681
7253
  EBK_PROTECTED_REF: \${{ github.ref }}
6682
7254
  run: |
6683
7255
  case "$EBK_STAGE:$EBK_PROTECTED_REF" in
6684
- staging:refs/heads/develop|production:refs/heads/main) ;;
7256
+ ${protectedWorkflowStageBranchCases}) ;;
6685
7257
  *) echo 'Secret metadata stage does not match the protected branch'; exit 1 ;;
6686
7258
  esac
6687
7259
  case "$EBK_PROJECT" in
6688
7260
  ''|-*|*-|*[!a-z0-9-]*) echo 'Secret metadata project must be an exact normalized Slice name'; exit 1 ;;
6689
7261
  esac
6690
- test -n "$AWS_DEPLOY_ROLE_ARN" || { echo "$EBK_STAGE AWS_DEPLOY_ROLE_ARN is required"; exit 1; }
6691
- test -n "$AWS_EXPECTED_ACCOUNT_ID" || { echo "$EBK_STAGE AWS_EXPECTED_ACCOUNT_ID is required"; exit 1; }
6692
- test -n "$AWS_REGION" || { echo "$EBK_STAGE AWS_REGION is required"; exit 1; }
6693
- case "$AWS_DEPLOY_ROLE_ARN" in
6694
- arn:aws:iam::$AWS_EXPECTED_ACCOUNT_ID:role/*) ;;
6695
- *) echo 'Secret metadata role does not belong to the expected account'; exit 1 ;;
6696
- esac
6697
7262
  - uses: ${checkoutAction}
6698
7263
  with:
6699
7264
  persist-credentials: false
@@ -6705,24 +7270,153 @@ jobs:
6705
7270
  - run: pnpm install --frozen-lockfile --ignore-scripts --ignore-pnpmfile
6706
7271
  - name: Verify managed workspace integrity before AWS authority
6707
7272
  run: pnpm nx run ${name}:workspace-health
7273
+ - id: policy
7274
+ name: Derive committed secret metadata stage policy
7275
+ run: |
7276
+ ${indentWorkflowScript(protectedSecretPolicyDerivationScript(), 10)}
7277
+ - id: authority
7278
+ name: Derive canonical committed stage authority
7279
+ env:
7280
+ EBK_DEVELOPMENT_ACCOUNT: \${{ steps.policy.outputs.development_account_id }}
7281
+ EBK_PRODUCTION_ACCOUNT: \${{ steps.policy.outputs.production_account_id }}
7282
+ EBK_REGION: \${{ steps.policy.outputs.deployment_region }}
7283
+ EBK_STAGING_ACCOUNT: \${{ steps.policy.outputs.staging_account_id }}
7284
+ run: |
7285
+ ${indentWorkflowScript(`set -euo pipefail\n${(0, workspace_files_thin_deploy_js_1.canonicalStageAuthorityDerivationScript)()}`, 10)}
6708
7286
  - uses: ${configureAwsAction}
6709
7287
  with:
6710
- role-to-assume: \${{ secrets.AWS_DEPLOY_ROLE_ARN }}
7288
+ audience: \${{ steps.authority.outputs.audience }}
7289
+ role-to-assume: \${{ steps.authority.outputs.role_arn }}
6711
7290
  role-session-name: ebk-secret-metadata-\${{ github.run_id }}-\${{ github.run_attempt }}
6712
- aws-region: \${{ vars.AWS_REGION }}
6713
- - name: Verify assumed account
6714
- run: test "$(aws sts get-caller-identity --query Account --output text)" = "$AWS_EXPECTED_ACCOUNT_ID"
7291
+ aws-region: \${{ steps.policy.outputs.deployment_region }}
7292
+ - name: Verify exact stage authority
7293
+ env:
7294
+ AWS_DEPLOY_ROLE_ARN: \${{ steps.authority.outputs.role_arn }}
7295
+ AWS_PARTITION: \${{ steps.authority.outputs.partition }}
7296
+ EBK_POLICY_ACCOUNT_ID: \${{ steps.authority.outputs.account_id }}
7297
+ run: |
7298
+ ${indentWorkflowScript((0, workspace_files_thin_deploy_js_1.exactStageDeployerGuardScript)('Secret metadata', '$EBK_POLICY_ACCOUNT_ID'), 10)}
6715
7299
  - name: Reconcile value-blind metadata
7300
+ env:
7301
+ AWS_DEPLOY_ROLE_ARN: \${{ steps.authority.outputs.role_arn }}
7302
+ AWS_EXPECTED_ACCOUNT_ID: \${{ steps.authority.outputs.account_id }}
7303
+ AWS_REGION: \${{ steps.policy.outputs.deployment_region }}
6716
7304
  run: |
6717
7305
  args=("--stage=$EBK_STAGE" "--region=$AWS_REGION")
6718
7306
  if [ "$EBK_ACTION" = apply ]; then args+=("--apply"); fi
6719
7307
  pnpm nx run "$EBK_PROJECT:secret-metadata" -- "\${args[@]}"
6720
7308
  `,
6721
- '.github/workflows/sync-back.yml': `name: Sync production history
7309
+ '.github/workflows/secret-initialize.yml': `name: Secret initialize
7310
+
7311
+ on:
7312
+ workflow_dispatch:
7313
+ inputs:
7314
+ stage:
7315
+ description: Protected stage
7316
+ required: true
7317
+ type: choice
7318
+ options: [${protectedWorkflowStageChoices}]
7319
+ project:
7320
+ description: Exact Product Slice project
7321
+ required: true
7322
+ type: string
7323
+ logical_name:
7324
+ description: Exact machine-generated secret logical name
7325
+ required: true
7326
+ type: string
7327
+ action:
7328
+ description: Value-blind initialization action
7329
+ required: true
7330
+ default: report
7331
+ type: choice
7332
+ options: [report, apply]
7333
+
7334
+ concurrency:
7335
+ group: secret-initialize-\${{ inputs.stage }}-\${{ inputs.project }}-\${{ inputs.logical_name }}
7336
+ cancel-in-progress: false
7337
+
7338
+ permissions:
7339
+ contents: read
7340
+
7341
+ jobs:
7342
+ initialize:
7343
+ environment: \${{ inputs.stage }}
7344
+ permissions:
7345
+ contents: read
7346
+ id-token: write
7347
+ env:
7348
+ EBK_ACTION: \${{ inputs.action }}
7349
+ EBK_LOGICAL_NAME: \${{ inputs.logical_name }}
7350
+ EBK_PROJECT: \${{ inputs.project }}
7351
+ EBK_RENDERED_PROFILE: ${deploymentProfile}
7352
+ EBK_STAGE: \${{ inputs.stage }}
7353
+ runs-on: ubuntu-latest
7354
+ steps:
7355
+ - name: Validate protected request
7356
+ env:
7357
+ EBK_PROTECTED_REF: \${{ github.ref }}
7358
+ run: |
7359
+ case "$EBK_STAGE:$EBK_PROTECTED_REF" in
7360
+ ${protectedWorkflowStageBranchCases}) ;;
7361
+ *) echo 'Secret initialize stage does not match the protected branch'; exit 1 ;;
7362
+ esac
7363
+ case "$EBK_PROJECT" in
7364
+ ''|-*|*-|*[!a-z0-9-]*) echo 'Secret initialize project must be an exact normalized Slice name'; exit 1 ;;
7365
+ esac
7366
+ [[ "$EBK_LOGICAL_NAME" =~ ^[A-Za-z][A-Za-z0-9]{0,63}$ ]] ||
7367
+ { echo 'Secret initialize logical name is invalid'; exit 1; }
7368
+ - uses: ${checkoutAction}
7369
+ with:
7370
+ persist-credentials: false
7371
+ - uses: ${pnpmAction}
7372
+ - uses: ${setupNodeAction}
7373
+ with:
7374
+ node-version: ${tool_versions_js_1.WORKSPACE_NODE_VERSION}
7375
+ cache: pnpm
7376
+ - run: pnpm install --frozen-lockfile --ignore-scripts --ignore-pnpmfile
7377
+ - name: Verify managed workspace integrity before AWS authority
7378
+ run: pnpm nx run ${name}:workspace-health
7379
+ - id: policy
7380
+ name: Derive committed secret initialize stage policy
7381
+ run: |
7382
+ ${indentWorkflowScript(protectedSecretPolicyDerivationScript(), 10)}
7383
+ - id: authority
7384
+ name: Derive canonical committed stage authority
7385
+ env:
7386
+ EBK_DEVELOPMENT_ACCOUNT: \${{ steps.policy.outputs.development_account_id }}
7387
+ EBK_PRODUCTION_ACCOUNT: \${{ steps.policy.outputs.production_account_id }}
7388
+ EBK_REGION: \${{ steps.policy.outputs.deployment_region }}
7389
+ EBK_STAGING_ACCOUNT: \${{ steps.policy.outputs.staging_account_id }}
7390
+ run: |
7391
+ ${indentWorkflowScript(`set -euo pipefail\n${(0, workspace_files_thin_deploy_js_1.canonicalStageAuthorityDerivationScript)()}`, 10)}
7392
+ - uses: ${configureAwsAction}
7393
+ with:
7394
+ audience: \${{ steps.authority.outputs.audience }}
7395
+ role-to-assume: \${{ steps.authority.outputs.role_arn }}
7396
+ role-session-name: ebk-secret-initialize-\${{ github.run_id }}-\${{ github.run_attempt }}
7397
+ aws-region: \${{ steps.policy.outputs.deployment_region }}
7398
+ - name: Verify exact stage authority
7399
+ env:
7400
+ AWS_DEPLOY_ROLE_ARN: \${{ steps.authority.outputs.role_arn }}
7401
+ AWS_PARTITION: \${{ steps.authority.outputs.partition }}
7402
+ EBK_POLICY_ACCOUNT_ID: \${{ steps.authority.outputs.account_id }}
7403
+ run: |
7404
+ ${indentWorkflowScript((0, workspace_files_thin_deploy_js_1.exactStageDeployerGuardScript)('Secret initialize', '$EBK_POLICY_ACCOUNT_ID'), 10)}
7405
+ - name: Initialize machine-generated secret value-blind
7406
+ env:
7407
+ AWS_DEPLOY_ROLE_ARN: \${{ steps.authority.outputs.role_arn }}
7408
+ AWS_EXPECTED_ACCOUNT_ID: \${{ steps.authority.outputs.account_id }}
7409
+ AWS_REGION: \${{ steps.policy.outputs.deployment_region }}
7410
+ run: |
7411
+ args=("--logicalName=$EBK_LOGICAL_NAME" "--stage=$EBK_STAGE" "--region=$AWS_REGION")
7412
+ if [ "$EBK_ACTION" = apply ]; then args+=("--apply"); fi
7413
+ pnpm nx run "$EBK_PROJECT:secret-initialize" -- "\${args[@]}"
7414
+ `,
7415
+ '.github/workflows/sync-back.yml': `name: Sync protected main history
6722
7416
 
6723
7417
  on:
6724
7418
  workflow_run:
6725
- workflows: [Deploy]
7419
+ workflows: [Verify]
6726
7420
  types: [completed]
6727
7421
 
6728
7422
  permissions:
@@ -6731,8 +7425,12 @@ permissions:
6731
7425
 
6732
7426
  jobs:
6733
7427
  sync-back:
7428
+ # The sync carries history, not deployment evidence: open it after any
7429
+ # completed main run so a failed production deploy cannot lock develop
7430
+ # behind a sync that never opens.
6734
7431
  if: >-
6735
- github.event.workflow_run.conclusion == 'success' &&
7432
+ github.event.workflow_run.conclusion != 'cancelled' &&
7433
+ github.event.workflow_run.event == 'push' &&
6736
7434
  github.event.workflow_run.head_branch == 'main'
6737
7435
  runs-on: ubuntu-latest
6738
7436
  steps:
@@ -6746,8 +7444,8 @@ jobs:
6746
7444
  gh pr create \
6747
7445
  --head main \
6748
7446
  --base develop \
6749
- --title 'chore: sync production history to develop' \
6750
- --body 'Managed EBK sync-back. Review and merge; this workflow never pushes or auto-merges.'
7447
+ --title 'chore(git): sync protected main to develop' \
7448
+ --body 'Managed EBK source-history sync. Review and merge; this does not claim or waive independent staging or production deployment evidence, and this workflow never pushes or auto-merges.'
6751
7449
  number=$(gh pr list --state open --head main --base develop --json number --jq '.[0].number')
6752
7450
  fi
6753
7451
  echo "Sync-back pull request #$number is ready for review."
@@ -6764,7 +7462,7 @@ jobs:
6764
7462
  fi
6765
7463
  sleep 5
6766
7464
  done
6767
- echo 'No pull-request verification run materialized. Close and reopen the sync pull request as a human, then approve its workflows.'
7465
+ echo "No pull-request verification run materialized; events created with GITHUB_TOKEN may not start workflows. Close and reopen sync pull request #$number with a user credential (a write-access user, or an agent in that user's own gh session: gh pr close $number && gh pr reopen $number), then approve its workflows if GitHub marks them approval-required."
6768
7466
  exit 1
6769
7467
  `,
6770
7468
  'tools/ebk-infrastructure-plan-summary.mjs': `import {
@@ -7005,9 +7703,15 @@ infrastructure or protected deployment policy. Preserve the ownership boundary r
7005
7703
  \`.ebk/ownership.json\`: managed files belong to EBK, seeded files are generated starting
7006
7704
  points that adopters may change, and unrecorded files are adopter-owned.
7007
7705
 
7008
- Secret values are a human-only TTY ceremony. Agents and CI must never invoke
7009
- \`ebk secret set\` or \`ebk secret remove\`; they may use only value-blind
7010
- metadata, status, and verification commands.
7706
+ Ordinary feature work uses affected tests, typecheck, lint, and ownership checks.
7707
+ At a local milestone, \`pnpm verify\` checks current source through
7708
+ \`${name}:verify-proposed\`, including before the first Git commit. This is not
7709
+ protected Blueprint acceptance. The separate \`${name}:verify\` target and
7710
+ protected GitHub workflows retain their committed-history and trust gates.
7711
+
7712
+ Human-supplied secret values are entered only by a human. The Developer-Agent workflow
7713
+ below describes the hand-off for \`ebk secret set\` and \`ebk secret remove\` and the
7714
+ value-blind paths agents and CI use.
7011
7715
 
7012
7716
  ## Work markers
7013
7717
 
@@ -7067,40 +7771,6 @@ export default tseslint.config(
7067
7771
  rules: { 'no-console': 'error' },
7068
7772
  },
7069
7773
  );
7070
- `,
7071
- 'README.md': `# ${name}
7072
-
7073
- This workspace was created with Empire Builder Kit v1.
7074
-
7075
- ## Start here
7076
-
7077
- 1. Review \`docs/architecture.md\`.
7078
- 2. Review the generated Foundation before deploying it.
7079
- 3. Add product slices through the Empire Builder Kit Nx plugin.
7080
- 4. Review \`docs/repository-operating-policy.md\` and \`docs/git-workflow.md\` before enabling production.
7081
- 5. Read \`AGENTS.md\` and \`docs/work-markers.md\` before assigning generated customization work to agents.
7082
-
7083
- For one-origin fleet development, initialize the workspace-pinned CLI once with
7084
- \`ebk dev configure --profile=<bounded-development-profile>\`, inspect the certificate reported
7085
- by \`ebk dev setup\`, and then explicitly approve \`ebk dev setup --trust\`. The supported v1
7086
- origin uses the complete explicit-port HTTPS URL reported at startup; optional
7087
- \`--portless\` setup is experimental. \`ebk dev doctor\` requires a real exact-host TLS
7088
- handshake for trust and connectivity readiness, and reports portless access ready
7089
- only after a live port-443 probe. Complete
7090
- \`aws sso login --profile <bounded-development-profile>\`, then start selected Slices with
7091
- \`ebk dev up billing accounts\`. The CLI derives the personal stage from the operating-system
7092
- account, reads the saved profile and Region, propagates that AWS context to child Slices, and
7093
- uses SST \`mono\` mode so agents and non-cursor-addressable terminals receive ordinary logs.
7094
- Use \`ebk dev status\`, \`ebk dev logs\`, \`ebk dev tail\`, \`ebk dev restart\`,
7095
- \`ebk dev down\`, and \`ebk dev doctor\` without supplying a stage. Setup, doctor, and the
7096
- managed root Fleet target remain internal Nx-backed implementation surfaces; \`ebk dev\` is the
7097
- framework-local development interface. For the development namespace, if the shell does not
7098
- expose workspace binaries, \`pnpm exec ebk dev ...\` only locates the workspace-pinned binary;
7099
- it is not another framework command surface. Protected Blueprint receipt review must instead
7100
- use the direct installed \`.bin/ebk blueprint verification-review\` entry documented in
7101
- \`docs/blueprints.md\`.
7102
-
7103
- Workspace creation only writes source files. It does not create or change cloud resources.
7104
7774
  `,
7105
7775
  'docs/architecture.md': `# Workspace architecture
7106
7776
 
@@ -7114,248 +7784,18 @@ The Standard Foundation lives at \`packages/foundation\`. Product slices live be
7114
7784
  \`packages/<slice-name>\` and consume versioned Foundation contracts instead of importing
7115
7785
  Foundation implementation files.
7116
7786
 
7117
- Canonical consumer stages use the matching \`development\`, \`staging\`, or \`production\`
7118
- Foundation. Framework-derived personal stages may use only the \`development\`
7119
- Foundation; generic preview stages are unsupported.
7120
- `,
7121
- 'docs/git-workflow.md': `# Protected Git workflow
7122
-
7123
- Ordinary work starts from \`develop\` and returns through a reviewed pull request to
7124
- \`develop\`. A production promotion is exactly \`develop\` to \`main\`. After production,
7125
- review and merge the managed \`main\` to \`develop\` sync-back pull request before ordinary
7126
- work resumes.
7127
-
7128
- Both protected branches require the stable \`policy\` and \`verify\` checks, one approving
7129
- review including an approval from the committed catch-all CODEOWNER and someone other than the
7130
- most recent pusher, dismissed stale reviews, resolved conversations, and merge commits. Replace
7131
- the placeholder in \`.github/CODEOWNERS\` with a GitHub user or team that has repository access
7132
- before bootstrap. An approval from \`github-actions[bot]\` cannot satisfy the independent
7133
- code-owner gate; the Actions repository setting is needed to create the managed sync-back pull
7134
- request, not to approve it.
7135
- Direct updates, force pushes, deletion, squash merging, and rebase merging are prohibited.
7136
- GitHub rulesets, environments, and AWS IAM are the authorization boundaries; local hooks are
7137
- only defense in depth.
7138
-
7139
- The \`staging\` environment accepts only \`develop\`; \`production\` accepts only \`main\`.
7140
- The committed \`productionApproval.mode\` selects one of two production authorization
7141
- contracts:
7142
-
7143
- - \`promotion-pr\` is compatible with private repositories on GitHub Team. The required
7144
- non-author CODEOWNER approval on the exact \`develop\` to \`main\` promotion pull request
7145
- is the production authorization. Merging that pull request starts production deployment;
7146
- there is no second EBK-managed environment approval or wait timer. The approver may also be
7147
- the human whose merge triggers deployment. Roll back through a reviewed revert on the
7148
- protected branches and let the resulting \`main\` push redeploy. A repository write actor who
7149
- can run Actions may also manually dispatch \`deploy\`, \`refresh\`, or \`unlock\` against the
7150
- already approved \`main\` tip without another human gate. That residual cannot introduce
7151
- unreviewed source; production \`remove\` remains separately variable-gated. Restrict write and
7152
- Actions access, audit dispatches, or select \`environment-reviewer\` when every production
7153
- action needs separate approval.
7154
- - \`environment-reviewer\` adds a separate production environment reviewer with self-review
7155
- prevention. A private repository needs GitHub Enterprise capability for this mode. Bootstrap
7156
- inspects that capability and fails closed on an unsupported plan; it never silently changes
7157
- the committed mode.
7158
-
7159
- Both modes retain required checks, a non-author CODEOWNER approval, no ruleset bypass, the
7160
- literal production environment, and its branch-scoped credentials. The base \`staging\` and
7161
- \`production\` environments own deployment, smoke, and ECR evidence roles plus their
7162
- variables; production also owns the staging-registry promotion pull role. Dedicated
7163
- \`infrastructure-plan-<stage>\` environments gate read-only role use. Separate
7164
- \`async-recovery-<stage>\`, \`performance-aggregate-<stage>\`,
7165
- \`performance-cost-<stage>\`, \`performance-load-<stage>\`, and
7166
- \`reliability-read-<stage>\` environments own only the role and variables required by that
7167
- capability. Infrastructure-plan jobs use a repository-variable read-only role/input inventory;
7168
- an independent clean audit job assumes that exact environment-subject role and verifies its
7169
- trust and complete inline policy before any repository-backed plan job may assume it. Every
7170
- \`*-staging\` environment accepts only \`develop\`; every
7171
- \`*-production\` environment accepts only \`main\`; reviewer, self-review, and wait protection
7172
- matches the corresponding base stage. The credential-free \`npm-prerelease\` environment
7173
- accepts only \`main\` and contains no secrets or variables. In Team-compatible
7174
- \`promotion-pr\` mode, protected-main review plus the exact current-main tag binding is
7175
- publication authorization, so it has no reviewer or wait rule. In
7176
- \`environment-reviewer\` mode it mirrors production protection. Never add repository- or
7177
- organization-visible fallbacks for protected values. Keep repository default workflow
7178
- permissions read-only; generated jobs request only the additional permissions they need.
7179
-
7180
- \`EBK_FLEET_ORIGIN\` is the sole protected deployment and smoke origin. Read-only plan jobs
7181
- enter only their dedicated \`infrastructure-plan-<stage>\` environment; their stage-named
7182
- repository variables \`EBK_STAGING_FLEET_ORIGIN\` and
7183
- \`EBK_PRODUCTION_FLEET_ORIGIN\` remain plan inputs only. Before mutation, each
7184
- base-environment-protected deployment requires
7185
- its \`EBK_FLEET_ORIGIN\` to equal the origin retained by the plan.
7186
-
7187
- \`AWS_ASYNC_RECOVERY_ROLE_ARN\` is a separate mutation role. Trust only this
7188
- repository's exact \`async-recovery-<stage>\` GitHub OIDC subject and grant only
7189
- the recovery operations/resources declared by generated capability adapters.
7190
-
7191
- \`AWS_PERFORMANCE_LOAD_ROLE_ARN\` is a separate read-only OIDC role. Trust only
7192
- this repository's exact \`performance-load-<stage>\` GitHub OIDC subject.
7193
- Set that environment's \`EBK_PERFORMANCE_ORIGINS_JSON\` to a JSON object that
7194
- maps every reviewed Slice to the exact \`performanceEndpoint\` emitted by its
7195
- current deployment; never copy the map to repository scope or substitute the
7196
- fleet web origin. The workflow selects only its requested Slice, and the
7197
- committed load plan must name that same origin and exact reserved Slice path.
7198
- Grant \`sts:GetCallerIdentity\` and \`secretsmanager:GetSecretValue\` only on
7199
- \`arn:<partition>:secretsmanager:<region>:<account>:secret:ebk/<stage>/<slice>/*\` for
7200
- reviewed Slices. Do not grant secret writes, deployment, CloudFormation, SST,
7201
- or CloudWatch Logs access. The protected workflow never logs or uploads HMAC
7202
- material and removes its new job-scoped 0600 file after cleanup.
7203
- Bootstrap verifies environment-only secret placement, not the role's hidden
7204
- AWS trust or permission policy.
7205
-
7206
- \`AWS_PERFORMANCE_AGGREGATE_ROLE_ARN\` is a separate adopter-owned read-only
7207
- OIDC role. Trust only this repository's exact
7208
- \`performance-aggregate-<stage>\` GitHub OIDC subject. Grant
7209
- \`sts:GetCallerIdentity\` plus only the exact data-read actions and resource
7210
- ARNs required by the adopted \`performance/aggregate-count-provider.ts\`.
7211
- Do not grant writes, deployment, CloudFormation, SST, IAM, Cost Explorer, or
7212
- unrelated data access. The generated workflow validates the protected Deploy
7213
- artifact before role assumption, verifies the STS account and environment
7214
- Region, withholds the OIDC bearer, and clears the AWS session before artifact
7215
- retention. Bootstrap verifies environment-only secret placement, not the
7216
- role's hidden AWS trust or least-privilege permission policy.
7217
-
7218
- \`AWS_PERFORMANCE_COST_ROLE_ARN\` is a separate read-only OIDC role. Trust only
7219
- this repository's exact \`performance-cost-<stage>\` GitHub OIDC subject.
7220
- Grant only \`sts:GetCallerIdentity\`, \`ce:ListCostAllocationTags\`, and
7221
- \`ce:GetCostAndUsage\`; Cost Explorer
7222
- queries are account-global, while the retained evidence still binds the
7223
- selected resource Region. Do not grant budget, anomaly-monitor, tag, billing,
7224
- deployment, CloudFormation, SST, or other mutation authority. Bootstrap verifies
7225
- environment-only secret placement, not the role's hidden AWS trust or
7226
- permission policy.
7227
-
7228
- The framework manages
7229
- \`.github/workflows/performance-aggregate-counts.yml\`, its exact Deploy
7230
- binding, evidence wrapper, artifact name, and canonical schema. The seeded
7231
- \`performance/aggregate-count-provider.ts\` remains adopter-owned and fails
7232
- closed until it returns only the reviewed business counts and provider request
7233
- reference. The provider runs only with the dedicated aggregate-read role after
7234
- the framework validates the exact protected release and STS account. The
7235
- workflow binds those untrusted counts to the environment-owned account/Region,
7236
- closed period, exact current protected release, Slice, stage, and workspace
7237
- and records the dedicated OIDC access mode and adopter-owned least-privilege
7238
- read contract before it emits the single
7239
- \`aggregate-count-<stage>-<slice>-<run>-<attempt>\` artifact. The framework
7240
- does not invent counts or copy them from request-level dimensions.
7241
-
7242
- \`performance-cost-actual.yml\` treats supplied run IDs and artifact names only
7243
- as selectors. Before OIDC it queries GitHub for the fixed aggregate workflow
7244
- and generated \`Deploy\` workflow runs, requiring the same repository,
7245
- candidate, protected branch, successful conclusion, allowed event, exact
7246
- workflow name/path, and one exact nonexpired artifact. It derives the compact
7247
- performance release binding from that Deploy artifact and separately verifies
7248
- the independently approved aggregate-count and release-binding byte digests.
7249
-
7250
- \`AWS_RELIABILITY_READ_ROLE_ARN\` is a separate read-only OIDC role. Trust only
7251
- this repository's exact \`reliability-read-<stage>\` GitHub OIDC subject.
7252
- Grant \`sts:GetCallerIdentity\`, \`cloudwatch:DescribeAlarms\`,
7253
- \`cloudwatch:DescribeAlarmHistory\`, \`cloudwatch:GetMetricData\`,
7254
- \`ecs:DescribeServices\`, and \`states:DescribeExecution\`. Scope ECS and Step
7255
- Functions resources to the reviewed Slice services, state machines, and
7256
- executions wherever IAM supports resource-level constraints. Do not grant log
7257
- reads, deployment, CloudFormation, SST, or any AWS mutation.
7258
-
7259
- The generated \`reliability-read.yml\` workflow selects the newest nonexpired
7260
- binding artifact from a successful deployed-smoke run, reconciles it to the
7261
- current successful protected deployment, and checks out that exact commit
7262
- before assuming the role. Callers choose only a bounded operation and workload;
7263
- they cannot supply the AWS account, Region, role, or binding path. The binding
7264
- allowlists exact alarm names, API ID, ECS services, and Step Functions
7265
- resources before the adapter makes a service call. Bootstrap verifies
7266
- environment-only secret placement, not the role's hidden AWS trust or
7267
- permission policy.
7268
-
7269
- The async-recovery, performance-load, performance-cost, and reliability-read
7270
- jobs have job-scoped \`id-token: write\`. They install the frozen dependency
7271
- graph with lifecycle scripts and pnpm hooks disabled before assuming AWS
7272
- authority. Immediately after the one pinned credential action, they blank the
7273
- OIDC request URL and bearer for future steps and explicitly shadow both values
7274
- empty on every later step. After bounded AWS use, they blank current AWS
7275
- credential variables before evidence upload. This withholds credentials from
7276
- later processes; it does not claim that GitHub revokes the job-level
7277
- permission.
7278
-
7279
- \`AWS_SMOKE_ROLE_ARN\` is separate from deployment authority. Trust only this
7280
- repository's GitHub OIDC subject for the matching protected environment. Grant
7281
- only run-scoped Cognito principal setup/cleanup on reviewed user-pool ARNs and
7282
- \`logs:FilterLogEvents\` on exact
7283
- \`/ebk/<stage>/<slice>/event-consumer\` log-group ARNs. Do not grant
7284
- CloudFormation, SST mutation, broad Logs query, \`GetLogEvents\`,
7285
- \`StartQuery\`, or \`logs:Unmask\`. Bootstrap verifies environment-only secret
7286
- inventory but never creates or reads the secret, materializes the role, or
7287
- attests its hidden policy.
7288
-
7289
- Run the GitHub bootstrap in report mode first. Source upgrades never alter remote GitHub
7290
- settings. Apply a reviewed report only after \`develop\` exists and the candidate's exact
7291
- \`policy\` and \`verify\` check contexts have run.
7292
-
7293
- Set \`GH_REPO=<owner>/<repository>\` and a suitable \`GH_TOKEN\`, then run
7294
- \`pnpm nx run ${name}:github-bootstrap\`. Review the deterministic report before rerunning
7295
- with \`--apply\`. The command never creates branches, credentials, secrets, or AWS resources.
7296
- Supply \`--npmPrereleaseReviewerIds=<id,...>\` only in
7297
- \`environment-reviewer\` mode when intentionally setting a distinct npm
7298
- mutation reviewer set.
7299
- Supply \`--productionReviewerIds=<id,...>\` only when committed policy selects
7300
- \`environment-reviewer\`; it is rejected in \`promotion-pr\` mode.
7301
-
7302
- Install the optional defense-in-depth pre-push hook explicitly with
7303
- \`pnpm nx run ${name}:install-git-hooks\`. It refuses to overwrite an unrelated existing hook.
7304
-
7305
- The sync-back workflow uses only \`GITHUB_TOKEN\` and never pushes or auto-merges. GitHub may
7306
- place its pull-request workflows in an approval-required state. Approve them; if no run
7307
- materializes, close and reopen the pull request as a human and then approve it.
7308
- `,
7309
- 'docs/repository-operating-policy.md': `# Repository Operating Policy
7310
-
7311
- \`ebk.repository-policy.json\` is the reviewed source of truth for production access,
7312
- production approval, per-Slice readiness, and the one shared production Foundation profile.
7313
- New workspaces start with production disabled and explicit Team-compatible
7314
- \`productionApproval.mode: "promotion-pr"\`. A missing approval mode or Slice entry fails
7315
- closed; no GitHub capability is allowed to choose a mode implicitly.
7316
-
7317
- The named repository modes are source-writing presets, not runtime state. Report a change
7318
- first and inspect the complete current/next policy:
7319
-
7320
- \`\`\`bash
7321
- pnpm nx g @empire-builder-kit/nx:repository-policy \\
7322
- --preset=controlled-production --slices=billing
7323
- \`\`\`
7324
-
7325
- Apply the reviewed source change separately with \`--apply\`. This command never contacts AWS
7326
- or GitHub. Use \`--status=blocked|pilot|ready\` for explicit Slice transitions and
7327
- \`--profile=cost-optimized|standard\` for an explicit shared Foundation decision.
7328
- Use \`--productionApprovalMode=promotion-pr|environment-reviewer\` for an explicit approval
7329
- transition. This changes committed policy only; a separate bootstrap report/apply reconciles
7330
- remote GitHub settings.
7331
-
7332
- Validation requires both an explicit production approval mode and an explicit Foundation
7333
- profile. It does not infer omitted values. Use \`promotion-pr\` for a private GitHub Team
7334
- repository. Use \`environment-reviewer\` only when the repository plan can enforce a separate
7335
- environment reviewer.
7336
-
7337
- - \`blocked\` prohibits production create, update, and migration.
7338
- - \`pilot\` permits protected evidence gathering without claiming normal latency or maturity.
7339
- - \`ready\` records an adopter decision after its evidence and review; it is not inferred.
7340
- - removal and unlock remain explicit protected recovery paths so disabled policy cannot strand
7341
- resources.
7342
-
7343
- Protected production deploy and refresh validate the complete Foundation/Slice set before AWS
7344
- access. The Foundation is always ordered first. The set is derived from committed policy and
7345
- cannot be narrowed with an ad hoc project argument.
7346
-
7347
- \`standard\` keeps Aurora warm at 2–16 ACU. \`cost-optimized\` is a prerelease candidate at
7348
- 0–4 ACU with a five-minute auto-pause and potential cold-resume latency. Both keep managed NAT,
7349
- 30-day backups, deletion protection, retained data, and the protected GitHub/OIDC/AWS path.
7350
- Changing policy affects only later lifecycle requests; it never deploys or removes resources.
7351
- Changing profile requires a separate reviewed Foundation deployment and retained operational
7352
- evidence.
7353
-
7354
- \`EBK_PRODUCTION_FOUNDATION_PROFILE\` is reserved managed executor input. The lifecycle
7355
- executor derives it from validated committed policy immediately before SST and removes any
7356
- ambient shell value. Do not set it as a local, repository, organization, or environment
7357
- variable. Incubation keeps the last configured profile so protected remove and unlock can still
7358
- render existing production resources; production deploy remains disabled.
7787
+ This workspace's \`${deploymentProfile}\` profile activates only:
7788
+ ${activeStages.join(', ')}. Each active canonical consumer stage uses the matching Foundation.
7789
+ Inactive canonical stages are rejected. Framework-derived personal stages may use only the
7790
+ \`development\` Foundation; generic preview stages are unsupported.
7791
+
7792
+ Every active canonical stage owns a separate Router/domain, Cognito user pool,
7793
+ event bus, secret namespace, IAM boundary, logs, queues, ECR namespace, and
7794
+ stage-qualified SSM discovery contract. Discovery rejects a record for an inactive or different
7795
+ canonical stage. DynamoDB tables belong to individual Slices/tasks, not one global application
7796
+ table. Declared relational features add an independent Aurora cluster per canonical stage,
7797
+ with separate logical databases and migration histories per Slice. Existing relational resources
7798
+ remain declared on upgrade and after removing their last consumer; that is not database retirement.
7359
7799
  `,
7360
7800
  'docs/work-markers.md': `# Agent work markers
7361
7801
 
@@ -7415,8 +7855,22 @@ deployment by themselves. Open \`SECURITY\` and \`BLOCKER\` markers fail staging
7415
7855
  deploy or refresh before AWS access. Protected remove and unlock remain usable for recovery.
7416
7856
  There is no local or silent bypass in this prerelease contract; any future waiver mechanism
7417
7857
  must be committed, reviewable, and auditable.
7858
+
7859
+ ## Shared-operation claims
7860
+
7861
+ \`${workspace_files_shared_operation_claims_js_1.SHARED_OPERATION_CLAIMS_PATH}\` is a seeded claim ledger for operations that only one
7862
+ actor at a time may run: gateway setup, package release, candidate adoption, database
7863
+ replacement, and development deployment. Before starting one, an agent or human reads the
7864
+ current ledger where the other actors record claims; if another open claim names the same
7865
+ operation, it stops and reports the conflict instead of running the operation. Otherwise it
7866
+ records one \`EBK-TODO\` claim with the reference
7867
+ \`PLAN:${workspace_files_shared_operation_claims_js_1.SHARED_OPERATION_CLAIM_PHASE}/<operation>\`, commits it where the others read the
7868
+ ledger, and deletes the claim when the operation and its verification are complete. The
7869
+ work-markers target above lists open claims. Nothing enforces a claim; the ledger is a
7870
+ convention, current only as far as the copy each actor reads.
7418
7871
  `,
7419
- 'ebk.repository-policy.json': json((0, index_js_4.createIncubationRepositoryPolicy)()),
7872
+ [workspace_files_shared_operation_claims_js_1.SHARED_OPERATION_CLAIMS_PATH]: (0, workspace_files_shared_operation_claims_js_1.renderSharedOperationClaims)(name),
7873
+ 'ebk.repository-policy.json': json(initialRepositoryPolicy),
7420
7874
  'nx.json': json({
7421
7875
  $schema: './node_modules/nx/schemas/nx-schema.json',
7422
7876
  defaultBase: 'develop',
@@ -7456,6 +7910,7 @@ must be committed, reviewable, and auditable.
7456
7910
  inputs: ['default', '^production'],
7457
7911
  },
7458
7912
  },
7913
+ analytics: false,
7459
7914
  neverConnectToCloud: true,
7460
7915
  useDaemonProcess: false,
7461
7916
  }),
@@ -7471,23 +7926,25 @@ must be committed, reviewable, and auditable.
7471
7926
  scripts: {
7472
7927
  build: `nx run-many -t build --exclude=${name}`,
7473
7928
  format: 'nx format:write',
7474
- 'format:check': 'nx format:check',
7929
+ 'format:check': 'nx format:check --all',
7475
7930
  graph: 'nx graph',
7476
7931
  health: `nx run ${name}:workspace-health`,
7477
7932
  lint: `nx run-many -t lint --exclude=${name}`,
7478
7933
  test: `nx run-many -t test --exclude=${name}`,
7479
7934
  typecheck: `nx run-many -t typecheck --exclude=${name}`,
7480
- verify: `nx run ${name}:verify && nx run-many -t verify --exclude=${name}`,
7935
+ verify: `nx format:check --all && nx run ${name}:verify-proposed && nx run-many -t verify --exclude=${name}`,
7481
7936
  },
7482
7937
  dependencies: {
7483
7938
  '@empire-builder-kit/runtime': frameworkVersion,
7484
7939
  },
7485
7940
  devDependencies: {
7486
7941
  '@aws-sdk/client-iam': TOOL_VERSIONS.awsSdk,
7942
+ '@aws-sdk/client-rds': TOOL_VERSIONS.awsSdk,
7487
7943
  '@aws-sdk/client-s3': TOOL_VERSIONS.awsSdk,
7488
7944
  '@aws-sdk/client-secrets-manager': TOOL_VERSIONS.awsSdk,
7489
7945
  '@aws-sdk/client-ssm': TOOL_VERSIONS.awsSdk,
7490
7946
  '@aws-sdk/client-sts': TOOL_VERSIONS.awsSdk,
7947
+ '@aws-sdk/credential-providers': TOOL_VERSIONS.awsSdk,
7491
7948
  '@eslint/js': TOOL_VERSIONS.eslintJs,
7492
7949
  '@empire-builder-kit/nx': frameworkVersion,
7493
7950
  '@types/node': TOOL_VERSIONS.nodeTypes,
@@ -7520,6 +7977,15 @@ must be committed, reviewable, and auditable.
7520
7977
  protectedRef: 'refs/remotes/origin/develop',
7521
7978
  },
7522
7979
  },
7980
+ 'blueprint-trust-check-main': {
7981
+ executor: '@empire-builder-kit/nx:blueprint-trust',
7982
+ options: {
7983
+ operation: 'adopt',
7984
+ output: '.ebk/evidence/supply-chain/blueprint-trust.json',
7985
+ protectedBranch: 'main',
7986
+ protectedRef: 'refs/remotes/origin/main',
7987
+ },
7988
+ },
7523
7989
  'blueprint-trust-check-protected': {
7524
7990
  executor: '@empire-builder-kit/nx:blueprint-trust',
7525
7991
  options: {
@@ -7562,6 +8028,23 @@ must be committed, reviewable, and auditable.
7562
8028
  executor: 'nx:run-commands',
7563
8029
  options: { command: 'node tools/install-ebk-hooks.mjs' },
7564
8030
  },
8031
+ 'stage-authority-render': {
8032
+ cache: false,
8033
+ executor: 'nx:run-commands',
8034
+ outputs: ['{workspaceRoot}/.ebk/authority/{args.stage}/*'],
8035
+ options: {
8036
+ command: 'node tools/stage-authority-render.mjs',
8037
+ forwardAllArgs: true,
8038
+ },
8039
+ },
8040
+ 'deploy-local': {
8041
+ cache: false,
8042
+ dependsOn: ['workspace-health'],
8043
+ executor: '@empire-builder-kit/nx:local-deploy',
8044
+ options: {
8045
+ stage: 'development',
8046
+ },
8047
+ },
7565
8048
  'supply-chain-check': {
7566
8049
  executor: 'nx:run-commands',
7567
8050
  options: {
@@ -7585,64 +8068,27 @@ must be committed, reviewable, and auditable.
7585
8068
  executor: '@empire-builder-kit/nx:work-markers',
7586
8069
  options: {},
7587
8070
  },
7588
- 'workload-role-candidate-report': {
7589
- executor: 'nx:run-commands',
7590
- options: {
7591
- command: 'node tools/workload-role-baseline/report-candidate-bundle.mjs',
7592
- forwardAllArgs: true,
7593
- },
7594
- },
7595
- 'workload-role-provisioner-build': {
7596
- executor: 'nx:run-commands',
7597
- options: {
7598
- command: 'node tools/workload-role-baseline/build.mjs',
7599
- },
7600
- },
7601
8071
  verify: {
7602
8072
  dependsOn: ['blueprint-trust-check', 'supply-chain-check'],
7603
8073
  executor: 'nx:run-commands',
7604
8074
  options: { command: 'node tools/workspace-health.mjs' },
7605
8075
  },
8076
+ 'verify-proposed': {
8077
+ dependsOn: ['supply-chain-check'],
8078
+ executor: 'nx:run-commands',
8079
+ options: { command: 'node tools/workspace-health.mjs' },
8080
+ },
7606
8081
  'workspace-health': {
7607
8082
  executor: 'nx:run-commands',
7608
8083
  options: { command: 'node tools/workspace-health.mjs' },
7609
8084
  },
7610
8085
  },
7611
8086
  }),
7612
- 'packages/foundation/README.md': `# Standard Foundation
7613
-
7614
- This project owns the workspace-wide AWS platform for each canonical \`development\`,
7615
- \`staging\`, or \`production\` stage. It is a separate SST state boundary from product slices.
7616
- Never deploy it at a framework-derived personal stage; that consumer maps to
7617
- \`development\`.
7618
-
7619
- It provides the v1 network, routing, container compute, Postgres, identity, events,
7620
- observability, configuration, and deployment capabilities. Product slices consume its
7621
- versioned contract; they do not import files from \`infra/\`.
7622
-
7623
- Workspace creation does not deploy this project. Review \`docs/costs.md\`,
7624
- \`docs/security.md\`, and \`docs/runbooks.md\` before the first deployment.
7625
-
7626
- Set \`EBK_ROOT_DOMAIN\` before deploying when Route 53 in the target account owns a public
7627
- domain. Without it, the shared Router uses its functional provider URL.
7628
- `,
7629
- 'packages/foundation/contracts/foundation.json': json({
7630
- contractVersion: '1.0.0',
7631
- capabilities: [
7632
- 'network',
7633
- 'routing',
7634
- 'container-compute',
7635
- 'postgres',
7636
- 'identity',
7637
- 'events',
7638
- 'observability',
7639
- 'configuration',
7640
- 'deployment',
7641
- ],
7642
- }),
8087
+ 'packages/foundation/contracts/foundation.json': json(foundationPlan.descriptor),
7643
8088
  'packages/foundation/contracts/costs.json': json({
7644
- schemaVersion: 2,
8089
+ schemaVersion: 3,
7645
8090
  aurora: {
8091
+ declared: foundationPlan.resources.postgres,
7646
8092
  engine: index_js_4.FOUNDATION_AURORA_ENGINE_FAMILY,
7647
8093
  engineVersion: index_js_4.FOUNDATION_AURORA_ENGINE_VERSION,
7648
8094
  },
@@ -7650,19 +8096,29 @@ domain. Without it, the shared Router uses its functional provider URL.
7650
8096
  productionProfileSource: 'ebk.repository-policy.json',
7651
8097
  stages: {
7652
8098
  development: {
7653
- aurora: 'scale-to-zero',
8099
+ aurora: foundationPlan.resources.postgres
8100
+ ? 'scale-to-zero'
8101
+ : 'not-declared',
7654
8102
  dataRetention: 'retained',
7655
- nat: 'ec2',
7656
- },
7657
- staging: {
7658
- aurora: 'scale-to-zero',
7659
- dataRetention: 'retained',
7660
- nat: 'ec2',
8103
+ nat: foundationPlan.resources.network ? 'ec2' : 'none',
7661
8104
  },
8105
+ ...(deploymentProfile === 'starter'
8106
+ ? {}
8107
+ : {
8108
+ staging: {
8109
+ aurora: foundationPlan.resources.postgres
8110
+ ? 'scale-to-zero'
8111
+ : 'not-declared',
8112
+ dataRetention: 'retained',
8113
+ nat: foundationPlan.resources.network ? 'ec2' : 'none',
8114
+ },
8115
+ }),
7662
8116
  production: {
7663
- aurora: 'repository-policy',
8117
+ aurora: foundationPlan.resources.postgres
8118
+ ? 'repository-policy'
8119
+ : 'not-declared',
7664
8120
  dataRetention: 'retained-and-protected',
7665
- nat: 'managed',
8121
+ nat: foundationPlan.resources.network ? 'managed' : 'none',
7666
8122
  },
7667
8123
  },
7668
8124
  usagePriced: [
@@ -7677,68 +8133,55 @@ domain. Without it, the shared Router uses its functional provider URL.
7677
8133
  'packages/foundation/contracts/frontend-security-headers.json': json((0, index_js_2.foundationFrontendSecurityHeaderPolicy)()),
7678
8134
  'packages/foundation/docs/costs.md': `# Foundation cost contract
7679
8135
 
7680
- The VPC NAT path and Aurora capacity are the main fixed-cost decisions.
8136
+ The default Foundation has no Aurora, VPC/NAT, or ECS cluster. Slice-scoped DynamoDB
8137
+ is the first persistence choice. A relational requirement adds Aurora and its
8138
+ network/container migration dependencies; an explicit network workload can also
8139
+ require those dependencies without SQL. Previously provisioned capabilities stay
8140
+ declared until an explicit retirement operation, even with no remaining consumers.
7681
8141
  The machine-readable stage inventory is \`../contracts/costs.json\`; keep this
7682
8142
  seeded decision record aligned with organization-specific budgets and alerts.
7683
8143
 
7684
- - Development uses EC2 NAT, a development bastion, and Aurora scale-to-zero.
7685
- - Staging uses EC2 NAT and Aurora scale-to-zero by default.
7686
- - Production always uses managed NAT and retains protected data. Its explicit Repository
7687
- Operating Policy profile selects either the standard always-warm Aurora floor or the
7688
- prerelease cost-optimized auto-pause candidate.
8144
+ - When network/SQL is declared, development uses EC2 NAT, a development bastion,
8145
+ and Aurora scale-to-zero (${(0, cost_estimate_js_1.nonproductionAuroraScalingLabel)('development')}).
8146
+ ${deploymentProfile === 'starter' ? '' : `- Declared staging network/SQL uses EC2 NAT and Aurora scale-to-zero (${(0, cost_estimate_js_1.nonproductionAuroraScalingLabel)('staging')}) by default.\n`}- Declared production networking uses managed NAT; production retains protected data. Its explicit Repository
8147
+ Operating Policy profile selects cost-optimized (0–2 ACUs, pause after five idle
8148
+ minutes), balanced (0.5–4 ACUs, always warm), or standard (2–16 ACUs, always warm).
8149
+ Newly generated repositories default to cost-optimized in every deployment profile.
8150
+ Cold resume adds latency; measure it for the workload and validate
8151
+ connection retries, request timeouts, migrations, and background polling.
8152
+ Capacity is shared by all Slices. Open connections can prevent pausing; storage
8153
+ and managed NAT remain billable while Aurora compute is paused.
8154
+ Existing 0.5–4 cost-optimized adopters must explicitly select balanced before
8155
+ upgrade, then opt into cost-optimized separately if cold starts are acceptable.
7689
8156
  - ECS, EventBridge, Cognito, SNS, SSM, and CloudWatch are primarily usage-priced; logs,
7690
8157
  metrics, alarms, and retained data still require active cost review.
8158
+ - Every deployed stage of every Standard Slice, including each personal development
8159
+ stage, adds its own fixed floor: a KMS key, secrets, a machine-to-machine app client,
8160
+ CloudWatch alarms and a dashboard. The estimate below lists it.
7691
8161
 
7692
8162
  Run \`foundation:remove\` only for disposable stages. Production is retained and protected by
7693
8163
  the SST configuration.
7694
- `,
7695
- 'packages/foundation/docs/runbooks.md': `# Foundation runbooks
7696
-
7697
- ## Deployment
7698
8164
 
7699
- Use Nx for every supported lifecycle action:
7700
-
7701
- - \`pnpm nx run foundation:deploy --stage=development\`
7702
- - \`pnpm nx run foundation:refresh --stage=development\`
7703
- - \`pnpm nx run foundation:unlock --stage=development\`
7704
- - \`pnpm nx run foundation:remove --stage=development\`
7705
-
7706
- Do not invoke SST lifecycle commands directly. Production deployment belongs in the trusted
7707
- CI path.
7708
-
7709
- Foundation stages are canonical. \`ebk dev\` derives a Product Slice personal
7710
- stage from the operating-system account and maps that consumer to the
7711
- development Foundation. Callers cannot select that stage. Slice preflight
7712
- reports both identities, never deploys a personal Foundation, and full mapped
7713
- personal-stage deploys fail closed.
7714
-
7715
- ## Database restore
7716
-
7717
- Production Aurora retains 30 days of automated backups; non-production retains 7 days.
7718
- Restore into a new cluster, validate application migrations and data integrity, then cut over
7719
- consumers deliberately. Never expose the Foundation master credential to a product slice.
7720
-
7721
- ## Protected database migrations
7722
-
7723
- Each database-backed Product Slice deploys a one-off Fargate migration task and
7724
- Standard Step Functions workflow into the shared cluster. The existing
7725
- environment-scoped \`AWS_DEPLOY_ROLE_ARN\` starts and observes that exact
7726
- workflow; do not create a second migration OIDC role. GitHub has no direct SQL,
7727
- database-secret-value, or \`rds-db:connect\` authority. The generated task role
7728
- connects as the Slice's no-password IAM-authenticated migrator user.
7729
- `,
8165
+ ${(0, cost_estimate_js_1.renderFoundationCostEstimate)({ deploymentProfile, headingLevel: 2 })}`,
7730
8166
  'packages/foundation/docs/security.md': `# Foundation security contract
7731
8167
 
7732
8168
  - Production resources are retained and protected.
7733
- - The shared Aurora master credential is Foundation-internal.
7734
- - Aurora IAM authentication is enabled for generated short-lived migration
8169
+ - When relational persistence is declared, the shared Aurora master credential is Foundation-internal.
8170
+ - Declared Aurora IAM authentication is enabled for generated short-lived migration
7735
8171
  tasks. The exact cluster resource ID is non-secret Foundation discovery.
7736
- - Product slices receive scoped logical-database credentials through the provisioning contract.
8172
+ - Relational Product Slices receive scoped logical-database credentials through the provisioning contract.
8173
+ - DynamoDB tables and their access are Slice/task scoped; no shared Foundation application table exists.
7737
8174
  - Cognito owns authentication; product slices own authorization policy and app clients.
7738
8175
  - Lambda and server-side web workloads use resource linking. Containers use the documented
7739
8176
  stage-scoped SSM/secret path and least-privilege IAM.
7740
8177
  - Public domains are configured explicitly through \`EBK_ROOT_DOMAIN\`; generated source never
7741
8178
  contains credentials or customer domain assumptions.
8179
+ - Every stage's CloudFront Router has its own global Web ACL. The baseline always includes AWS
8180
+ Common Rule Set, Known Bad Inputs, Amazon IP Reputation, rate blocking, CloudWatch metrics,
8181
+ and sampled requests. Nonproduction is allowlisted by default. Production is public by
8182
+ default, but cannot deploy until the exact policy has passed a ${rehearsalStage} blocking rehearsal.
8183
+ - Full WAF request logging is disabled by default. Enabling it keeps blocked requests only,
8184
+ uses bounded retention, and redacts query strings, cookies, authorization, and API keys.
7742
8185
 
7743
8186
  The managed \`contracts/frontend-security-headers.json\` file owns the transport
7744
8187
  minimum: HTTPS in deployed environments, one-year HSTS there, and the common
@@ -7770,18 +8213,32 @@ declare const $app: {
7770
8213
 
7771
8214
  import {
7772
8215
  FOUNDATION_SSM_SCHEMA_VERSION,
7773
- STANDARD_FOUNDATION_CAPABILITIES,
8216
+ createRouterWafRules,
8217
+ createRouterWafStagePlan,
8218
+ ${foundationPlan.resources.postgres ? ' foundationDatabaseEndpointHost,' : ''}
7774
8219
  foundationSsmPath,
8220
+ normalizeRouterWafPolicy,
8221
+ reconcileRouterWafBlockingRehearsal,
8222
+ routerWafContractDigest,
7775
8223
  type FoundationSsmKey,
7776
8224
  } from '@empire-builder-kit/nx/foundation';
7777
8225
  import {
7778
- FOUNDATION_AURORA_ENGINE,
7779
- FOUNDATION_AURORA_ENGINE_VERSION,
8226
+ ${foundationPlan.resources.postgres ? ' FOUNDATION_AURORA_ENGINE,\n FOUNDATION_AURORA_ENGINE_VERSION,' : ''}
7780
8227
  productionFoundationProfileContract,
7781
8228
  } from '@empire-builder-kit/nx/repository-policy';
8229
+ import { installStageIamBoundaryTransforms } from '@empire-builder-kit/nx/aws-authority';
8230
+ import { createStageAuthorityIdentity } from '@empire-builder-kit/nx/aws-authority';
8231
+ import {
8232
+ configuredDeploymentAccountForStage,
8233
+ configuredDeploymentRegion,
8234
+ productionRehearsalStage,
8235
+ } from '@empire-builder-kit/nx/repository-policy';
7782
8236
  import { createFoundationAccountCostControlPlan } from '@empire-builder-kit/nx/performance';
7783
8237
  import accountCostPolicy from '../performance/account-cost-policy.json';
7784
- import type { FoundationWorkloadRolePreflight } from './workload-roles/preflight.js';
8238
+ import routerWafBlockingRehearsal from '../security/router-waf-blocking-rehearsal.json';
8239
+ import routerWafLiveEvidence from '../security/router-waf-live-evidence.json';
8240
+ import routerWafPolicy from '../security/router-waf.json';
8241
+ import repositoryPolicy from '../../../ebk.repository-policy.json';
7785
8242
 
7786
8243
  function identityDomainPrefix(stage: string, accountId: string): string {
7787
8244
  if (!/^\\d{12}$/.test(accountId)) {
@@ -7802,7 +8259,7 @@ function identityDomainPrefix(stage: string, accountId: string): string {
7802
8259
  return value.slice(0, 50) + '-' + digest;
7803
8260
  }
7804
8261
 
7805
- function publishFoundation(values: Record<FoundationSsmKey, any>) {
8262
+ function publishFoundation(values: Partial<Record<FoundationSsmKey, any>>) {
7806
8263
  return Object.fromEntries(
7807
8264
  Object.entries(values).map(([key, value]) => [
7808
8265
  key,
@@ -7815,45 +8272,78 @@ function publishFoundation(values: Record<FoundationSsmKey, any>) {
7815
8272
  );
7816
8273
  }
7817
8274
 
7818
- export function foundation(
7819
- workloadRoles?: FoundationWorkloadRolePreflight,
7820
- ) {
7821
- if (!$dev && !workloadRoles) {
7822
- throw new Error(
7823
- 'Protected Foundation workload-role preflight must complete before infrastructure construction.',
7824
- );
7825
- }
8275
+ export function foundation() {
7826
8276
  const isProduction = $app.stage === 'production';
7827
- const isStaging = $app.stage === 'staging';
8277
+ ${foundationPlan.resources.postgres ? " const isStaging = $app.stage === 'staging';" : ''}
7828
8278
  const rootDomain = process.env.EBK_ROOT_DOMAIN?.trim();
7829
8279
  const productionConfiguration = isProduction
7830
8280
  ? productionFoundationProfileContract(
7831
8281
  process.env.EBK_PRODUCTION_FOUNDATION_PROFILE,
7832
8282
  )
7833
8283
  : undefined;
7834
- const natMode = productionConfiguration?.contract.nat ?? 'ec2';
7835
- const natBinding = workloadRoles?.bindings.nat;
7836
- if (natMode === 'ec2' && natBinding) {
7837
- $transform(
7838
- aws.iam.InstanceProfile,
7839
- (args: any, opts: any, logicalName: string) => {
7840
- if (logicalName !== 'FoundationVpcNatInstanceProfile') return;
7841
- args.name = natBinding.instanceProfileName;
7842
- args.role = natBinding.roleName;
7843
- opts.import = natBinding.instanceProfileName;
7844
- },
8284
+ ${foundationPlan.resources.network ? " const natMode = productionConfiguration?.contract.nat ?? 'ec2';" : ''}
8285
+ const normalizedRouterWafPolicy = normalizeRouterWafPolicy(routerWafPolicy);
8286
+ if (repositoryPolicy.deploymentProfile !== normalizedRouterWafPolicy.deploymentProfile) {
8287
+ throw new Error('Router WAF policy profile differs from committed repository policy.');
8288
+ }
8289
+ const rehearsalStage = productionRehearsalStage(normalizedRouterWafPolicy.deploymentProfile);
8290
+ const rehearsalAccount = configuredDeploymentAccountForStage(
8291
+ normalizedRouterWafPolicy.deploymentProfile,
8292
+ repositoryPolicy.deploymentAccounts,
8293
+ rehearsalStage,
8294
+ );
8295
+ const rehearsalRegion = configuredDeploymentRegion(repositoryPolicy.deploymentRegion);
8296
+ const rehearsalRoleArn = createStageAuthorityIdentity({
8297
+ accountId: rehearsalAccount,
8298
+ partition: rehearsalRegion.startsWith('us-gov-') ? 'aws-us-gov' : 'aws',
8299
+ region: rehearsalRegion,
8300
+ stage: rehearsalStage,
8301
+ }).deploymentRoleArn;
8302
+ const wafPlan = createRouterWafStagePlan({
8303
+ policy: normalizedRouterWafPolicy,
8304
+ rehearsal: isProduction
8305
+ ? reconcileRouterWafBlockingRehearsal({
8306
+ evidence: routerWafLiveEvidence,
8307
+ expected: {
8308
+ accountId: rehearsalAccount,
8309
+ deploymentRoleArn: rehearsalRoleArn,
8310
+ region: rehearsalRegion,
8311
+ stage: rehearsalStage,
8312
+ wafContractDigest: routerWafContractDigest(normalizedRouterWafPolicy),
8313
+ workspace: '${name}',
8314
+ },
8315
+ policy: normalizedRouterWafPolicy,
8316
+ receipt: routerWafBlockingRehearsal,
8317
+ })
8318
+ : null,
8319
+ stage: $app.stage,
8320
+ });
8321
+ const deploymentAccount = process.env.AWS_EXPECTED_ACCOUNT_ID?.trim() ?? '';
8322
+ if (!/^\\d{12}$/.test(deploymentAccount)) {
8323
+ throw new Error(
8324
+ 'Foundation deployment requires the exact committed AWS account before infrastructure construction.',
7845
8325
  );
7846
8326
  }
8327
+ installStageIamBoundaryTransforms({
8328
+ accountId: deploymentAccount,
8329
+ instanceProfileResource: aws.iam.InstanceProfile,
8330
+ registerTransform: $transform,
8331
+ roleResource: aws.iam.Role,
8332
+ stage: ($dev ? 'development' : $app.stage) as
8333
+ | 'development'
8334
+ | 'staging'
8335
+ | 'production',
8336
+ });
7847
8337
 
7848
- const vpc = new sst.aws.Vpc('FoundationVpc', {
8338
+ ${foundationPlan.resources.network
8339
+ ? ` const vpc = new sst.aws.Vpc('FoundationVpc', {
7849
8340
  az: 2,
7850
8341
  bastion: $app.stage === 'development',
7851
8342
  nat:
7852
- natMode === 'ec2'
7853
- ? (natBinding?.vpc.nat ?? 'ec2')
7854
- : 'managed',
7855
- });
7856
- const cluster = new sst.aws.Cluster('FoundationCluster', { vpc });
8343
+ natMode === 'ec2' ? 'ec2' : 'managed',
8344
+ });`
8345
+ : ''}
8346
+ ${foundationPlan.resources.containerCompute ? " const cluster = new sst.aws.Cluster('FoundationCluster', { vpc });" : ''}
7857
8347
  const router = new sst.aws.Router('FoundationRouter', {
7858
8348
  ...(rootDomain
7859
8349
  ? {
@@ -7862,10 +8352,138 @@ export function foundation(
7862
8352
  aliases: ['*.' + rootDomain],
7863
8353
  dns: sst.aws.dns(),
7864
8354
  },
7865
- }
8355
+ }
7866
8356
  : {}),
8357
+ waf: {
8358
+ logging: wafPlan.logging,
8359
+ managedRules: {
8360
+ coreRuleSet: true,
8361
+ knownBadInputs: true,
8362
+ sqlInjection: false,
8363
+ },
8364
+ rateLimitPerIp: wafPlan.rateLimitPerIp,
8365
+ },
8366
+ transform: {
8367
+ cdn(args: any) {
8368
+ if (!args.webAclArn) {
8369
+ throw new Error(
8370
+ 'Foundation Router must attach its exact global Web ACL.',
8371
+ );
8372
+ }
8373
+ args.tags = {
8374
+ ...(args.tags ?? {}),
8375
+ 'ebk:managed-by': 'empire-builder-kit',
8376
+ 'ebk:owner': 'foundation',
8377
+ 'ebk:stage': wafPlan.stage,
8378
+ 'ebk:workspace': '${name}',
8379
+ };
8380
+ },
8381
+ waf(args: any, opts: any) {
8382
+ if (args.scope !== 'CLOUDFRONT') {
8383
+ throw new Error(
8384
+ 'Foundation Router Web ACL must use global CloudFront scope.',
8385
+ );
8386
+ }
8387
+ const ipSets: { ipv4Arn?: any; ipv6Arn?: any; smokeArn?: any } = {};
8388
+ if (wafPlan.ipv4Cidrs.length > 0) {
8389
+ ipSets.ipv4Arn = new aws.wafv2.IpSet(
8390
+ 'FoundationRouterIpv4Allowlist',
8391
+ {
8392
+ addresses: wafPlan.ipv4Cidrs,
8393
+ ipAddressVersion: 'IPV4',
8394
+ name: 'foundation-' + wafPlan.stage + '-router-ipv4-allowlist',
8395
+ scope: 'CLOUDFRONT',
8396
+ tags: {
8397
+ 'ebk:managed-by': 'empire-builder-kit',
8398
+ 'ebk:owner': 'foundation',
8399
+ 'ebk:policy-digest': wafPlan.policyDigest,
8400
+ 'ebk:stage': wafPlan.stage,
8401
+ 'ebk:workspace': '${name}',
8402
+ },
8403
+ },
8404
+ { provider: opts.provider },
8405
+ ).arn;
8406
+ }
8407
+ if (wafPlan.ipv6Cidrs.length > 0) {
8408
+ ipSets.ipv6Arn = new aws.wafv2.IpSet(
8409
+ 'FoundationRouterIpv6Allowlist',
8410
+ {
8411
+ addresses: wafPlan.ipv6Cidrs,
8412
+ ipAddressVersion: 'IPV6',
8413
+ name: 'foundation-' + wafPlan.stage + '-router-ipv6-allowlist',
8414
+ scope: 'CLOUDFRONT',
8415
+ tags: {
8416
+ 'ebk:managed-by': 'empire-builder-kit',
8417
+ 'ebk:owner': 'foundation',
8418
+ 'ebk:policy-digest': wafPlan.policyDigest,
8419
+ 'ebk:stage': wafPlan.stage,
8420
+ 'ebk:workspace': '${name}',
8421
+ },
8422
+ },
8423
+ { provider: opts.provider },
8424
+ ).arn;
8425
+ }
8426
+ if (wafPlan.access === 'allowlist') {
8427
+ // Smoke rotates WAF's LockToken outside Pulumi. Keep the existing
8428
+ // baseline IP sets untouched; AWS's Cloud Control handler owns the
8429
+ // separate ephemeral set's lifecycle without a cached lock token.
8430
+ const smokeAccess = new aws.cloudcontrol.Resource(
8431
+ 'FoundationRouterSmokeAccess',
8432
+ {
8433
+ typeName: 'AWS::WAFv2::IPSet',
8434
+ desiredState: JSON.stringify({
8435
+ Addresses: [],
8436
+ IPAddressVersion: 'IPV4',
8437
+ Name: 'foundation-' + wafPlan.stage + '-router-smoke-access',
8438
+ Scope: 'CLOUDFRONT',
8439
+ Tags: Object.entries({
8440
+ 'ebk:managed-by': 'empire-builder-kit',
8441
+ 'ebk:owner': 'foundation',
8442
+ 'ebk:purpose': 'private-smoke-access',
8443
+ 'ebk:stage': wafPlan.stage,
8444
+ 'ebk:workspace': '${name}',
8445
+ }).map(([Key, Value]) => ({ Key, Value })),
8446
+ }),
8447
+ },
8448
+ {
8449
+ provider: opts.provider,
8450
+ // This dedicated set has creation-only identity/ownership inputs.
8451
+ // Admission owns its addresses; the Web ACL owns policy binding.
8452
+ // Retain old inputs on upgrade too: Cloud Control patches arrays
8453
+ // by index, which is unsafe for service-reordered Tags (including
8454
+ // removing an older policy-digest tag). Never patch this document.
8455
+ ignoreChanges: ['desiredState'],
8456
+ },
8457
+ );
8458
+ ipSets.smokeArn = smokeAccess.properties.apply(
8459
+ (properties: string) => JSON.parse(properties).Arn,
8460
+ );
8461
+ }
8462
+ args.defaultAction = { allow: {} };
8463
+ args.rules = createRouterWafRules(wafPlan, ipSets);
8464
+ args.scope = 'CLOUDFRONT';
8465
+ args.tags = {
8466
+ ...(args.tags ?? {}),
8467
+ 'ebk:managed-by': 'empire-builder-kit',
8468
+ 'ebk:owner': 'foundation',
8469
+ 'ebk:policy-digest': wafPlan.policyDigest,
8470
+ 'ebk:stage': wafPlan.stage,
8471
+ 'ebk:workspace': '${name}',
8472
+ };
8473
+ args.visibilityConfig = {
8474
+ cloudwatchMetricsEnabled: true,
8475
+ metricName:
8476
+ 'Ebk' +
8477
+ wafPlan.stage[0].toUpperCase() +
8478
+ wafPlan.stage.slice(1) +
8479
+ 'FoundationRouterWaf',
8480
+ sampledRequestsEnabled: true,
8481
+ };
8482
+ },
8483
+ },
7867
8484
  });
7868
- const database = new sst.aws.Aurora('FoundationDatabase', {
8485
+ ${foundationPlan.resources.postgres
8486
+ ? ` const database = new sst.aws.Aurora('FoundationDatabase', {
7869
8487
  // SQL bootstrap and migrations run inside the VPC. Keep the public HTTPS
7870
8488
  // Data API surface disabled; protected GitHub roles also deny its actions.
7871
8489
  dataApi: false,
@@ -7887,6 +8505,47 @@ export function foundation(
7887
8505
  },
7888
8506
  vpc,
7889
8507
  });
8508
+ const databaseClusterEndpoint = database.nodes.cluster.endpoint.apply(
8509
+ foundationDatabaseEndpointHost,
8510
+ );
8511
+ const databaseReaderEndpoint = database.nodes.cluster.readerEndpoint.apply(
8512
+ foundationDatabaseEndpointHost,
8513
+ );
8514
+ // SST v4.17.1 creates a top-level LinkRef on the next tick and its Aurora
8515
+ // implementation unconditionally splits cluster endpoints. During a
8516
+ // governed refresh after an interrupted first deploy, those provider
8517
+ // outputs are legitimately absent. Preserve the normal link contract while
8518
+ // keeping that read-only recovery program total; a real deploy resolves the
8519
+ // exact endpoints before any discovery resource is created.
8520
+ database.getSSTLink = () => ({
8521
+ include: [
8522
+ sst.aws.permission({
8523
+ actions: ['secretsmanager:GetSecretValue'],
8524
+ resources: [database.secretArn],
8525
+ }),
8526
+ sst.aws.permission({
8527
+ actions: [
8528
+ 'rds-data:BatchExecuteStatement',
8529
+ 'rds-data:BeginTransaction',
8530
+ 'rds-data:CommitTransaction',
8531
+ 'rds-data:ExecuteStatement',
8532
+ 'rds-data:RollbackTransaction',
8533
+ ],
8534
+ resources: [database.clusterArn],
8535
+ }),
8536
+ ],
8537
+ properties: {
8538
+ clusterArn: database.clusterArn,
8539
+ database: database.database,
8540
+ host: databaseClusterEndpoint,
8541
+ password: database.password,
8542
+ port: database.port,
8543
+ reader: databaseReaderEndpoint,
8544
+ secretArn: database.secretArn,
8545
+ username: database.username,
8546
+ },
8547
+ });`
8548
+ : ''}
7890
8549
  const foundationAccountId = aws.getCallerIdentityOutput({}).accountId;
7891
8550
  const foundationPartition = aws.getPartitionOutput({});
7892
8551
  const foundationRegion = aws.getRegionOutput({});
@@ -7915,11 +8574,12 @@ export function foundation(
7915
8574
  const accountCostControl = createFoundationAccountCostControlPlan({
7916
8575
  evaluatedAt: new Date().toISOString(),
7917
8576
  policy: accountCostPolicy,
8577
+ rehearsalStage: '${rehearsalStage}',
7918
8578
  region: process.env.AWS_REGION,
7919
8579
  stage: $app.stage,
7920
8580
  workspace: '${name}',
7921
8581
  });
7922
- const databaseInstanceId = database.nodes.instance.identifier;
8582
+ ${foundationPlan.resources.postgres ? ' const databaseInstanceId = database.nodes.instance.identifier;' : ''}
7923
8583
  const alertTopicPolicy = new aws.sns.TopicPolicy(
7924
8584
  'FoundationAlertTopicPolicy',
7925
8585
  {
@@ -7949,6 +8609,7 @@ export function foundation(
7949
8609
  },
7950
8610
  ],
7951
8611
  resources: [alertTopic.arn],
8612
+ sid: 'AllowBudgetAlerts',
7952
8613
  },
7953
8614
  {
7954
8615
  actions: ['sns:Publish'],
@@ -7962,8 +8623,10 @@ export function foundation(
7962
8623
  type: 'Service',
7963
8624
  }],
7964
8625
  resources: [alertTopic.arn],
8626
+ sid: 'AllowCostAnomalyAlerts',
7965
8627
  },
7966
- {
8628
+ ${foundationPlan.resources.postgres
8629
+ ? ` {
7967
8630
  actions: ['sns:Publish'],
7968
8631
  conditions: [
7969
8632
  {
@@ -7984,7 +8647,9 @@ export function foundation(
7984
8647
  type: 'Service',
7985
8648
  }],
7986
8649
  resources: [alertTopic.arn],
7987
- },
8650
+ sid: 'AllowRdsEventAlerts',
8651
+ },`
8652
+ : ''}
7988
8653
  {
7989
8654
  actions: ['sns:Publish'],
7990
8655
  conditions: [
@@ -8008,6 +8673,7 @@ export function foundation(
8008
8673
  },
8009
8674
  ],
8010
8675
  resources: [alertTopic.arn],
8676
+ sid: 'AllowCloudWatchAlarmAlerts',
8011
8677
  },
8012
8678
  ],
8013
8679
  }).json,
@@ -8036,7 +8702,43 @@ export function foundation(
8036
8702
  { dependsOn: [alertTopicPolicy] },
8037
8703
  )
8038
8704
  : undefined;
8039
- const databaseEvents = isProduction
8705
+ const accountCostAnomalyMonitor = isProduction
8706
+ ? new aws.costexplorer.AnomalyMonitor(
8707
+ 'FoundationAccountCostAnomalyMonitor',
8708
+ {
8709
+ monitorDimension: 'SERVICE',
8710
+ monitorType: 'DIMENSIONAL',
8711
+ name: '${name}-account-service-cost-anomalies',
8712
+ tags: {
8713
+ 'ebk:cost-policy-digest': accountCostControl.policyDigest,
8714
+ 'ebk:cost-scope': '${name}/account/production',
8715
+ 'ebk:owner': accountCostControl.owner,
8716
+ },
8717
+ },
8718
+ )
8719
+ : undefined;
8720
+ const accountCostAnomalySubscription =
8721
+ accountCostControl.anomaly && accountCostAnomalyMonitor
8722
+ ? new aws.costexplorer.AnomalySubscription(
8723
+ 'FoundationAccountCostAnomalySubscription',
8724
+ {
8725
+ frequency: accountCostControl.anomaly.subscription.frequency,
8726
+ monitorArnLists: [accountCostAnomalyMonitor.arn],
8727
+ name: accountCostControl.anomaly.subscription.name,
8728
+ subscribers: [{ address: alertTopic.arn, type: 'SNS' }],
8729
+ tags: {
8730
+ 'ebk:cost-policy-digest': accountCostControl.policyDigest,
8731
+ 'ebk:cost-scope': '${name}/account/production',
8732
+ 'ebk:owner': accountCostControl.owner,
8733
+ },
8734
+ thresholdExpression:
8735
+ accountCostControl.anomaly.subscription.thresholdExpression,
8736
+ },
8737
+ { dependsOn: [accountCostAnomalyMonitor, alertTopicPolicy] },
8738
+ )
8739
+ : undefined;
8740
+ ${foundationPlan.resources.postgres
8741
+ ? ` const databaseEvents = isProduction
8040
8742
  ? new aws.rds.EventSubscription(
8041
8743
  'FoundationDatabaseEvents',
8042
8744
  {
@@ -8118,7 +8820,14 @@ export function foundation(
8118
8820
  threshold: 90,
8119
8821
  treatMissingData: 'notBreaching',
8120
8822
  },
8121
- );
8823
+ );`
8824
+ : ` const dashboard = new aws.cloudwatch.Dashboard('FoundationDashboard', {
8825
+ dashboardName: 'ebk-' + $app.stage + '-foundation',
8826
+ dashboardBody: JSON.stringify({ widgets: [{
8827
+ type: 'text', x: 0, y: 0, width: 24, height: 3,
8828
+ properties: { markdown: '# Empire Builder Kit Foundation\\nStage: ' + $app.stage + '\\nPersistence: Slice-scoped DynamoDB; SQL not declared.' },
8829
+ }] }),
8830
+ });`}
8122
8831
 
8123
8832
  if (new Set(['development', 'staging', 'production']).has($app.stage)) {
8124
8833
  new aws.cloudwatch.EventArchive('FoundationEventArchive', {
@@ -8134,18 +8843,18 @@ export function foundation(
8134
8843
  accountCostControl.authority.monthlyMaximumUsd,
8135
8844
  ),
8136
8845
  alertTopicArn: alertTopic.arn,
8137
- capabilities: STANDARD_FOUNDATION_CAPABILITIES.join(','),
8138
- clusterId: cluster.id,
8139
- contractVersion: '1.0.0',
8140
- databaseClusterArn: database.clusterArn,
8141
- databaseClusterEndpoint: database.nodes.cluster.endpoint.apply(
8142
- (endpoint: string) => endpoint.split(':')[0],
8143
- ),
8846
+ capabilities: ${JSON.stringify(foundationPlan.descriptor.capabilities.join(','))},
8847
+ ${foundationPlan.resources.containerCompute ? ' clusterId: cluster.id,' : ''}
8848
+ contractVersion: '${foundationPlan.descriptor.contractVersion}',
8849
+ ${foundationPlan.resources.postgres
8850
+ ? ` databaseClusterArn: database.clusterArn,
8851
+ databaseClusterEndpoint,
8144
8852
  databaseClusterId: database.id,
8145
8853
  databaseClusterResourceId: database.nodes.cluster.clusterResourceId,
8146
- databaseHost: database.host,
8854
+ databaseHost: databaseClusterEndpoint,
8147
8855
  databaseMasterSecretArn: database.secretArn,
8148
- databasePort: database.port.apply(String),
8856
+ databasePort: database.port.apply(String),`
8857
+ : ''}
8149
8858
  eventBusArn: eventBus.arn,
8150
8859
  eventBusName: eventBus.name,
8151
8860
  hasPublicDomain: rootDomain ? 'true' : 'false',
@@ -8156,19 +8865,22 @@ export function foundation(
8156
8865
  routerDistributionId: router.distributionID,
8157
8866
  routerUrl: router.url,
8158
8867
  schemaVersion: String(FOUNDATION_SSM_SCHEMA_VERSION),
8159
- vpcId: vpc.id,
8868
+ ${foundationPlan.resources.network ? ' vpcId: vpc.id,' : ''}
8160
8869
  });
8161
8870
 
8162
8871
  return {
8163
8872
  accountBudgetName: accountBudget?.name,
8164
8873
  alertTopicArn: alertTopic.arn,
8165
- capabilities: STANDARD_FOUNDATION_CAPABILITIES,
8166
- clusterId: cluster.id,
8167
- contractVersion: '1.0.0',
8168
- databaseCapacityAlarmName: databaseCapacityAlarm.name,
8874
+ capabilities: ${JSON.stringify(foundationPlan.descriptor.capabilities)},
8875
+ ${foundationPlan.resources.containerCompute ? ' clusterId: cluster.id,' : ''}
8876
+ contractVersion: '${foundationPlan.descriptor.contractVersion}',
8877
+ costAnomalySubscriptionArn: accountCostAnomalySubscription?.arn,
8878
+ ${foundationPlan.resources.postgres ? ' databaseCapacityAlarmName: databaseCapacityAlarm.name,' : ''}
8169
8879
  dashboardName: dashboard.dashboardName,
8170
- databaseClusterId: database.id,
8171
- databaseEventSubscriptionId: databaseEvents?.id,
8880
+ ${foundationPlan.resources.postgres
8881
+ ? ` databaseClusterId: database.id,
8882
+ databaseEventSubscriptionId: databaseEvents?.id,`
8883
+ : ''}
8172
8884
  eventBusName: eventBus.name,
8173
8885
  identityUserPoolId: userPool.id,
8174
8886
  identityTokenEndpoint,
@@ -8176,33 +8888,68 @@ export function foundation(
8176
8888
  productionFoundationProfile: productionConfiguration?.profile,
8177
8889
  rootDomain,
8178
8890
  routerUrl: router.url,
8179
- vpcId: vpc.id,
8891
+ ${foundationPlan.resources.network ? ' vpcId: vpc.id,' : ''}
8180
8892
  };
8181
8893
  }
8182
8894
  `,
8183
8895
  'packages/foundation/infra/index.ts': `import { foundation } from './foundation.js';
8184
- import type { FoundationWorkloadRolePreflight } from './workload-roles/preflight.js';
8185
8896
 
8186
- export function app(workloadRoles?: FoundationWorkloadRolePreflight) {
8187
- const resources = foundation(workloadRoles);
8188
- return {
8189
- ...resources,
8190
- workloadRoleManifestDigest:
8191
- workloadRoles?.registry.manifestDigest ?? 'local-dev-ephemeral',
8192
- };
8897
+ export function app() {
8898
+ return foundation();
8193
8899
  }
8194
8900
  `,
8195
8901
  'packages/foundation/performance/account-cost-policy.json': json({
8196
8902
  actualNotificationThresholdPercent: 80,
8197
8903
  forecastNotificationThresholdPercent: 100,
8198
8904
  kind: 'ebk-foundation-account-cost-policy',
8199
- monthlyMaximumUsd: 1_000,
8905
+ monthlyMaximumUsd: deploymentProfile === 'starter' ? 250 : 1_000,
8200
8906
  owner: 'replace-with-owner',
8201
8907
  reviewAt: null,
8202
8908
  schemaVersion: 1,
8203
8909
  status: 'example',
8204
8910
  workspace: name,
8205
8911
  }),
8912
+ 'packages/foundation/security/router-waf.json': json({
8913
+ deploymentProfile,
8914
+ kind: 'ebk-router-waf-policy',
8915
+ logging: {
8916
+ enabled: false,
8917
+ retention: '1 week',
8918
+ },
8919
+ rateLimitPerIp: 2_000,
8920
+ schemaVersion: 1,
8921
+ stages: {
8922
+ development: {
8923
+ access: 'allowlist',
8924
+ allowedCidrs: [],
8925
+ mode: 'block',
8926
+ },
8927
+ production: {
8928
+ access: 'public',
8929
+ allowedCidrs: [],
8930
+ mode: 'block',
8931
+ },
8932
+ ...(deploymentProfile === 'starter'
8933
+ ? {}
8934
+ : {
8935
+ staging: {
8936
+ access: 'allowlist',
8937
+ allowedCidrs: [],
8938
+ mode: 'block',
8939
+ },
8940
+ }),
8941
+ },
8942
+ }),
8943
+ 'packages/foundation/security/router-waf-blocking-rehearsal.json': json({
8944
+ kind: 'ebk-router-waf-blocking-rehearsal',
8945
+ schemaVersion: 4,
8946
+ status: 'pending',
8947
+ }),
8948
+ 'packages/foundation/security/router-waf-live-evidence.json': json({
8949
+ kind: 'ebk-router-waf-live-evidence',
8950
+ schemaVersion: 4,
8951
+ status: 'pending',
8952
+ }),
8206
8953
  'packages/foundation/configuration/declarations.json': json({
8207
8954
  project: 'foundation',
8208
8955
  schemaVersion: 1,
@@ -8235,29 +8982,87 @@ export function app(workloadRoles?: FoundationWorkloadRolePreflight) {
8235
8982
  version: '4.17.1',
8236
8983
  },
8237
8984
  policy: {
8985
+ adoptedImports: [],
8238
8986
  additiveLowRiskTypes: [
8239
8987
  'aws:cloudwatch/dashboard:Dashboard',
8240
8988
  'aws:cloudwatch/logGroup:LogGroup',
8241
8989
  'aws:cloudwatch/metricAlarm:MetricAlarm',
8242
8990
  ],
8991
+ additiveReviewedTypes: [
8992
+ 'aws:cloudwatch/eventArchive:EventArchive',
8993
+ 'aws:cloudwatch/eventBus:EventBus',
8994
+ 'aws:cognito/userPoolDomain:UserPoolDomain',
8995
+ 'aws:ec2/defaultSecurityGroup:DefaultSecurityGroup',
8996
+ 'aws:ec2/internetGateway:InternetGateway',
8997
+ 'aws:ec2/routeTable:RouteTable',
8998
+ 'aws:ec2/routeTableAssociation:RouteTableAssociation',
8999
+ 'aws:ec2/securityGroup:SecurityGroup',
9000
+ 'aws:ec2/subnet:Subnet',
9001
+ 'aws:ec2/vpc:Vpc',
9002
+ 'aws:ecs/cluster:Cluster',
9003
+ 'aws:ecs/clusterCapacityProviders:ClusterCapacityProviders',
9004
+ 'aws:rds/clusterParameterGroup:ClusterParameterGroup',
9005
+ 'aws:rds/eventSubscription:EventSubscription',
9006
+ 'aws:rds/parameterGroup:ParameterGroup',
9007
+ 'aws:rds/subnetGroup:SubnetGroup',
9008
+ 'aws:servicediscovery/privateDnsNamespace:PrivateDnsNamespace',
9009
+ 'aws:sns/topic:Topic',
9010
+ 'aws:sns/topicPolicy:TopicPolicy',
9011
+ 'aws:wafv2/webAclLoggingConfiguration:WebAclLoggingConfiguration',
9012
+ 'pulumi-nodejs:dynamic:Resource',
9013
+ 'pulumi:providers:aws',
9014
+ 'pulumi:providers:pulumi-nodejs',
9015
+ 'pulumi:providers:random',
9016
+ 'pulumi:pulumi:Stack',
9017
+ 'sst:aws:Aurora',
9018
+ 'sst:aws:Bus',
9019
+ 'sst:aws:CDN',
9020
+ 'sst:aws:Cluster',
9021
+ 'sst:aws:CognitoUserPool',
9022
+ 'sst:aws:Router',
9023
+ 'sst:aws:SnsTopic',
9024
+ 'sst:aws:Vpc',
9025
+ 'sst:sst:LinkRef',
9026
+ 'sst:sst:Version',
9027
+ ],
8243
9028
  costOrQuotaTypes: [
8244
9029
  'aws:apigatewayv2/api:Api',
8245
9030
  'aws:budgets/budget:Budget',
9031
+ 'aws:cloudfront/cachePolicy:CachePolicy',
8246
9032
  'aws:cloudfront/distribution:Distribution',
9033
+ 'aws:cloudfront/function:Function',
9034
+ 'aws:cloudfront/keyValueStore:KeyValueStore',
9035
+ 'aws:costexplorer/anomalyMonitor:AnomalyMonitor',
9036
+ 'aws:costexplorer/anomalySubscription:AnomalySubscription',
8247
9037
  'aws:cognito/userPool:UserPool',
9038
+ 'aws:ec2/eip:Eip',
9039
+ 'aws:ec2/eipAssociation:EipAssociation',
9040
+ 'aws:ec2/instance:Instance',
8248
9041
  'aws:ec2/natGateway:NatGateway',
8249
9042
  'aws:rds/cluster:Cluster',
9043
+ 'aws:rds/clusterInstance:ClusterInstance',
9044
+ 'aws:wafv2/ipSet:IpSet',
9045
+ 'aws:wafv2/webAcl:WebAcl',
8250
9046
  ],
8251
9047
  failClosedOnUnknown: true,
8252
9048
  protectedPlanAuthority: 'deploy-role-residual',
8253
9049
  standingPolicyRiskClasses: ['no-change', 'additive-low-risk'],
8254
9050
  statefulTypes: [
9051
+ 'aws:cloudwatch/eventArchive:EventArchive',
9052
+ 'aws:cloudwatch/eventBus:EventBus',
8255
9053
  'aws:cognito/userPool:UserPool',
8256
9054
  'aws:dynamodb/table:Table',
8257
9055
  'aws:events/eventBus:EventBus',
8258
9056
  'aws:kms/key:Key',
8259
9057
  'aws:rds/cluster:Cluster',
8260
9058
  'aws:s3/bucket:Bucket',
9059
+ 'aws:secretsmanager/secret:Secret',
9060
+ 'aws:secretsmanager/secretVersion:SecretVersion',
9061
+ 'aws:sns/topic:Topic',
9062
+ 'aws:ssm/parameter:Parameter',
9063
+ 'aws:wafv2/ipSet:IpSet',
9064
+ 'aws:wafv2/webAcl:WebAcl',
9065
+ 'random:index/randomPassword:RandomPassword',
8261
9066
  ],
8262
9067
  },
8263
9068
  project: 'foundation',
@@ -8287,10 +9092,6 @@ export function app(workloadRoles?: FoundationWorkloadRolePreflight) {
8287
9092
  'verify',
8288
9093
  { target: 'profile-preflight', params: 'forward' },
8289
9094
  ],
8290
- outputs: [
8291
- `{projectRoot}/.ebk/evidence/infrastructure-convergence.json`,
8292
- `{projectRoot}/.ebk/evidence/infrastructure-mutation-binding.json`,
8293
- ],
8294
9095
  options: {
8295
9096
  cwd: 'packages/foundation',
8296
9097
  stage: 'development',
@@ -8307,15 +9108,7 @@ export function app(workloadRoles?: FoundationWorkloadRolePreflight) {
8307
9108
  'infrastructure-plan': {
8308
9109
  cache: false,
8309
9110
  executor: '@empire-builder-kit/nx:infrastructure-plan',
8310
- outputs: [
8311
- `{projectRoot}/.ebk/evidence/infrastructure-plan-authorization.json`,
8312
- `{projectRoot}/.ebk/evidence/infrastructure-plan-command.json`,
8313
- `{projectRoot}/.ebk/evidence/infrastructure-plan.private-stderr.txt`,
8314
- `{projectRoot}/.ebk/evidence/infrastructure-plan.raw.json`,
8315
- `{projectRoot}/.ebk/evidence/infrastructure-plan.json`,
8316
- ],
8317
9111
  options: {
8318
- action: 'deploy',
8319
9112
  cwd: 'packages/foundation',
8320
9113
  stage: 'development',
8321
9114
  stageMode: 'foundation',
@@ -8324,10 +9117,6 @@ export function app(workloadRoles?: FoundationWorkloadRolePreflight) {
8324
9117
  refresh: {
8325
9118
  executor: '@empire-builder-kit/nx:refresh',
8326
9119
  dependsOn: [{ target: 'profile-preflight', params: 'forward' }],
8327
- outputs: [
8328
- `{projectRoot}/.ebk/evidence/infrastructure-convergence.json`,
8329
- `{projectRoot}/.ebk/evidence/infrastructure-mutation-binding.json`,
8330
- ],
8331
9120
  options: {
8332
9121
  cwd: 'packages/foundation',
8333
9122
  stage: 'development',
@@ -8337,10 +9126,6 @@ export function app(workloadRoles?: FoundationWorkloadRolePreflight) {
8337
9126
  remove: {
8338
9127
  executor: '@empire-builder-kit/nx:remove',
8339
9128
  dependsOn: [{ target: 'remove-preflight', params: 'forward' }],
8340
- outputs: [
8341
- `{projectRoot}/.ebk/evidence/infrastructure-convergence.json`,
8342
- `{projectRoot}/.ebk/evidence/infrastructure-mutation-binding.json`,
8343
- ],
8344
9129
  options: {
8345
9130
  cwd: 'packages/foundation',
8346
9131
  stage: 'development',
@@ -8355,6 +9140,13 @@ export function app(workloadRoles?: FoundationWorkloadRolePreflight) {
8355
9140
  executor: '@empire-builder-kit/nx:foundation-profile-preflight',
8356
9141
  options: { stage: 'development' },
8357
9142
  },
9143
+ 'router-waf-rehearsal': {
9144
+ cache: false,
9145
+ executor: '@empire-builder-kit/nx:router-waf-rehearsal',
9146
+ outputs: [
9147
+ `{projectRoot}/security/router-waf-blocking-rehearsal.json`,
9148
+ ],
9149
+ },
8358
9150
  typecheck: {
8359
9151
  executor: 'nx:run-commands',
8360
9152
  options: {
@@ -8382,6 +9174,19 @@ export function app(workloadRoles?: FoundationWorkloadRolePreflight) {
8382
9174
  }),
8383
9175
  'packages/foundation/scripts/verify-foundation.mjs': `import { readFileSync } from 'node:fs';
8384
9176
 
9177
+ import {
9178
+ normalizeRouterWafBlockingRehearsal,
9179
+ normalizeRouterWafPolicy,
9180
+ reconcileRouterWafBlockingRehearsal,
9181
+ routerWafContractDigest,
9182
+ } from '@empire-builder-kit/nx/foundation';
9183
+ import { createStageAuthorityIdentity } from '@empire-builder-kit/nx/aws-authority';
9184
+ import {
9185
+ configuredDeploymentAccountForStage,
9186
+ configuredDeploymentRegion,
9187
+ productionRehearsalStage,
9188
+ } from '@empire-builder-kit/nx/repository-policy';
9189
+
8385
9190
  const descriptor = JSON.parse(
8386
9191
  readFileSync(new URL('../contracts/foundation.json', import.meta.url), 'utf8'),
8387
9192
  );
@@ -8397,35 +9202,92 @@ const frontendSecurityHeaders = JSON.parse(
8397
9202
  'utf8',
8398
9203
  ),
8399
9204
  );
8400
- const expectedCapabilities = [
8401
- 'network',
8402
- 'routing',
8403
- 'container-compute',
8404
- 'postgres',
8405
- 'identity',
8406
- 'events',
8407
- 'observability',
8408
- 'configuration',
8409
- 'deployment',
8410
- ];
9205
+ const routerWafPolicy = normalizeRouterWafPolicy(
9206
+ JSON.parse(
9207
+ readFileSync(
9208
+ new URL('../security/router-waf.json', import.meta.url),
9209
+ 'utf8',
9210
+ ),
9211
+ ),
9212
+ );
9213
+ const repositoryPolicy = JSON.parse(
9214
+ readFileSync(new URL('../../../ebk.repository-policy.json', import.meta.url), 'utf8'),
9215
+ );
9216
+ const workspaceManifest = JSON.parse(
9217
+ readFileSync(new URL('../../../package.json', import.meta.url), 'utf8'),
9218
+ );
9219
+ const rehearsalReceipt = JSON.parse(
9220
+ readFileSync(
9221
+ new URL(
9222
+ '../security/router-waf-blocking-rehearsal.json',
9223
+ import.meta.url,
9224
+ ),
9225
+ 'utf8',
9226
+ ),
9227
+ );
9228
+ const normalizedRehearsal = normalizeRouterWafBlockingRehearsal(rehearsalReceipt);
9229
+ if (normalizedRehearsal) {
9230
+ const rehearsalStage = productionRehearsalStage(repositoryPolicy.deploymentProfile);
9231
+ const rehearsalAccount = configuredDeploymentAccountForStage(
9232
+ repositoryPolicy.deploymentProfile,
9233
+ repositoryPolicy.deploymentAccounts,
9234
+ rehearsalStage,
9235
+ );
9236
+ const rehearsalRegion = configuredDeploymentRegion(repositoryPolicy.deploymentRegion);
9237
+ reconcileRouterWafBlockingRehearsal({
9238
+ evidence: JSON.parse(
9239
+ readFileSync(
9240
+ new URL('../security/router-waf-live-evidence.json', import.meta.url),
9241
+ 'utf8',
9242
+ ),
9243
+ ),
9244
+ expected: {
9245
+ accountId: rehearsalAccount,
9246
+ deploymentRoleArn: createStageAuthorityIdentity({
9247
+ accountId: rehearsalAccount,
9248
+ partition: rehearsalRegion.startsWith('us-gov-') ? 'aws-us-gov' : 'aws',
9249
+ region: rehearsalRegion,
9250
+ stage: rehearsalStage,
9251
+ }).deploymentRoleArn,
9252
+ region: rehearsalRegion,
9253
+ stage: rehearsalStage,
9254
+ wafContractDigest: routerWafContractDigest(routerWafPolicy),
9255
+ workspace: workspaceManifest.name,
9256
+ },
9257
+ policy: routerWafPolicy,
9258
+ receipt: normalizedRehearsal,
9259
+ });
9260
+ }
9261
+ const expectedCapabilities = ${JSON.stringify(foundationPlan.descriptor.capabilities)};
8411
9262
 
8412
- if (descriptor.contractVersion !== '1.0.0') {
8413
- throw new Error('Foundation contractVersion must be 1.0.0.');
9263
+ if (descriptor.contractVersion !== '2.0.0') {
9264
+ throw new Error('Foundation contractVersion must be 2.0.0.');
9265
+ }
9266
+ if (
9267
+ JSON.stringify(Object.keys(routerWafPolicy.stages).sort()) !==
9268
+ JSON.stringify(${JSON.stringify([...activeStages].sort())}) ||
9269
+ routerWafPolicy.stages.development.access !== 'allowlist' ||
9270
+ ${deploymentProfile !== 'starter' ? "routerWafPolicy.stages.staging?.access !== 'allowlist' ||" : ''}
9271
+ routerWafPolicy.stages.production.mode !== 'block'
9272
+ ) {
9273
+ throw new Error('Foundation Router WAF stage defaults are invalid.');
8414
9274
  }
8415
9275
  if (JSON.stringify(descriptor.capabilities) !== JSON.stringify(expectedCapabilities)) {
8416
- throw new Error('Foundation capability descriptor does not match the v1 standard.');
9276
+ throw new Error('Foundation capability descriptor does not match its generated resource declaration.');
8417
9277
  }
8418
9278
  if (
8419
- costs.schemaVersion !== 2 ||
9279
+ costs.schemaVersion !== 3 ||
9280
+ costs.aurora?.declared !== ${foundationPlan.resources.postgres} ||
8420
9281
  costs.aurora?.engineVersion !== '${index_js_4.FOUNDATION_AURORA_ENGINE_VERSION}' ||
8421
9282
  costs.productionProfileSource !== 'ebk.repository-policy.json' ||
8422
9283
  costs.productionProfiles?.standard?.aurora?.min !== '2 ACU' ||
8423
- costs.productionProfiles?.['cost-optimized']?.aurora?.min !== '0 ACU' ||
9284
+ JSON.stringify(costs.productionProfiles) !== JSON.stringify(${JSON.stringify(index_js_4.PRODUCTION_FOUNDATION_PROFILE_CONTRACTS)}) ||
8424
9285
  !costs.stages?.development ||
8425
- !costs.stages?.staging ||
9286
+ JSON.stringify(Object.keys(costs.stages ?? {}).sort()) !==
9287
+ JSON.stringify(${JSON.stringify([...activeStages].sort())}) ||
8426
9288
  costs.stages?.production?.dataRetention !== 'retained-and-protected'
8427
9289
  ) {
8428
- throw new Error('Foundation cost manifest does not match the v1 stage contract.');
9290
+ throw new Error('Foundation cost manifest does not match the declared capability and stage contract.');
8429
9291
  }
8430
9292
  const localHeaders =
8431
9293
  frontendSecurityHeaders.profiles?.['local-development']?.headers ?? [];
@@ -8462,9 +9324,9 @@ export {};
8462
9324
  export default $config({
8463
9325
  app(input: { stage?: string } | undefined) {
8464
9326
  const stage = input?.stage ?? 'development';
8465
- if (!['development', 'staging', 'production'].includes(stage)) {
9327
+ if (!new Set<string>(${JSON.stringify(activeStages)}).has(stage)) {
8466
9328
  throw new Error(
8467
- 'Foundation stage ' + stage + ' is not canonical. Use development, staging, or production.',
9329
+ 'Foundation stage ' + stage + ' is inactive for the ${deploymentProfile} deployment profile.',
8468
9330
  );
8469
9331
  }
8470
9332
  const production = stage === 'production';
@@ -8476,16 +9338,15 @@ export default $config({
8476
9338
  providers: {
8477
9339
  aws: {
8478
9340
  defaultTags: {
9341
+ // SST contributes sst:app and sst:stage. Keep the provider baseline
9342
+ // at eight tags so generated S3 asset objects remain within AWS's
9343
+ // hard ten-tag object limit. Richer performance tags stay explicit.
8479
9344
  tags: {
8480
9345
  'ebk:cost-allocation': 'foundation',
8481
9346
  'ebk:cost-scope': '${name}/foundation/' + stage,
8482
9347
  'ebk:framework-version': '${frameworkVersion}',
8483
9348
  'ebk:managed-by': 'empire-builder-kit',
8484
9349
  'ebk:owner': 'foundation',
8485
- 'ebk:production-profile':
8486
- process.env.EBK_PRODUCTION_FOUNDATION_PROFILE ?? 'standard',
8487
- 'ebk:repository':
8488
- process.env.GITHUB_REPOSITORY ?? '${name}',
8489
9350
  'ebk:slice': 'foundation',
8490
9351
  'ebk:stage': stage,
8491
9352
  'ebk:workspace': '${name}',
@@ -8497,15 +9358,8 @@ export default $config({
8497
9358
  };
8498
9359
  },
8499
9360
  async run() {
8500
- const workloadRoles = $dev
8501
- ? undefined
8502
- : await import(
8503
- './infra/workload-roles/preflight.js'
8504
- ).then(({ preflightFoundationWorkloadRoles }) =>
8505
- preflightFoundationWorkloadRoles($app.stage),
8506
- );
8507
9361
  const { app } = await import('./infra/index.js');
8508
- return app(workloadRoles);
9362
+ return app();
8509
9363
  },
8510
9364
  });
8511
9365
  `,
@@ -8520,6 +9374,7 @@ export default $config({
8520
9374
  ],
8521
9375
  exclude: ['.sst', 'dist', 'node_modules'],
8522
9376
  }),
9377
+ [dependency_overrides_js_1.DEPENDENCY_OVERRIDES_PATH]: json(dependencyOverrides),
8523
9378
  'pnpm-workspace.yaml': `packages:
8524
9379
  - 'packages/*'
8525
9380
  - 'packages/*/apps/*'
@@ -8554,6 +9409,7 @@ supportedArchitectures:
8554
9409
  strictDepBuilds: true
8555
9410
  allowBuilds:
8556
9411
  ${index_js_6.FRAMEWORK_REVIEWED_BUILD_PACKAGES.map((packageName) => ` '${packageName}': true`).join('\n')}
9412
+ ${(0, dependency_overrides_js_1.renderDependencyOverridesYaml)(dependencyOverrides)}
8557
9413
  `,
8558
9414
  'tsconfig.base.json': json({
8559
9415
  compilerOptions: {
@@ -9376,7 +10232,9 @@ process.stdout.write(
9376
10232
  );
9377
10233
  `,
9378
10234
  'tools/ebk-git-policy.mjs': `import { evaluateGitPolicy } from '@empire-builder-kit/nx/git-policy';
10235
+ import { deliveryLaneOf, readRepositoryOperatingPolicy } from '@empire-builder-kit/nx/repository-policy';
9379
10236
  import { spawnSync } from 'node:child_process';
10237
+ const deliveryLane = deliveryLaneOf(readRepositoryOperatingPolicy(process.cwd()));
9380
10238
 
9381
10239
  const git = (...args) => {
9382
10240
  const result = spawnSync('git', args, { encoding: 'utf8', shell: false });
@@ -9396,32 +10254,40 @@ if (!['pull_request', 'push', 'workflow_dispatch'].includes(eventName)) {
9396
10254
  throw new Error('Unsupported or missing GITHUB_EVENT_NAME; Git policy fails closed.');
9397
10255
  }
9398
10256
 
9399
- const request = { eventName, ref: process.env.GITHUB_REF };
10257
+ const request = { deliveryLane, eventName, ref: process.env.GITHUB_REF };
9400
10258
  if (eventName === 'pull_request' || eventName === 'push') {
9401
10259
  git(
9402
10260
  'fetch',
9403
10261
  '--no-tags',
9404
10262
  'origin',
9405
10263
  'main:refs/remotes/origin/main',
9406
- 'develop:refs/remotes/origin/develop',
10264
+ ...(deliveryLane === 'trunk' ? [] : ['develop:refs/remotes/origin/develop']),
9407
10265
  );
9408
10266
  }
9409
10267
  if (eventName === 'pull_request') {
9410
10268
  const baseRef = process.env.GITHUB_BASE_REF;
10269
+ const headRepository = process.env.EBK_HEAD_REPOSITORY;
9411
10270
  const headRef = process.env.GITHUB_HEAD_REF;
9412
- if (!baseRef || !headRef) {
9413
- throw new Error('Pull-request branch metadata is missing.');
10271
+ const repository = process.env.GITHUB_REPOSITORY;
10272
+ if (!baseRef || !headRepository || !headRef || !repository) {
10273
+ throw new Error('Pull-request branch and repository metadata is missing.');
9414
10274
  }
9415
10275
  Object.assign(request, {
9416
10276
  baseIsAncestorOfHead: isAncestor('refs/remotes/origin/' + baseRef, 'HEAD'),
9417
10277
  baseRef,
10278
+ headRepository,
9418
10279
  headRef,
9419
- mainIsAncestorOfDevelop: isAncestor(
9420
- 'refs/remotes/origin/main',
9421
- 'refs/remotes/origin/develop',
9422
- ),
10280
+ ...(deliveryLane === 'trunk'
10281
+ ? {}
10282
+ : {
10283
+ mainIsAncestorOfDevelop: isAncestor(
10284
+ 'refs/remotes/origin/main',
10285
+ 'refs/remotes/origin/develop',
10286
+ ),
10287
+ }),
10288
+ repository,
9423
10289
  });
9424
- } else if (eventName === 'push') {
10290
+ } else if (eventName === 'push' && deliveryLane !== 'trunk') {
9425
10291
  Object.assign(request, {
9426
10292
  baseIsAncestorOfHead: isAncestor('refs/remotes/origin/develop', 'HEAD'),
9427
10293
  mainIsAncestorOfDevelop: isAncestor(
@@ -9435,7 +10301,7 @@ const decision = evaluateGitPolicy(request);
9435
10301
  process.stdout.write(JSON.stringify(decision, null, 2) + '\\n');
9436
10302
  if (!decision.allowed) process.exitCode = 1;
9437
10303
  `,
9438
- 'tools/workspace-health.mjs': `import { validateRepositoryOperatingPolicy } from '@empire-builder-kit/nx/repository-policy';
10304
+ 'tools/workspace-health.mjs': `import { readRepositoryOperatingPolicy } from '@empire-builder-kit/nx/repository-policy';
9439
10305
  import { assertWorkMarkerReport, formatWorkMarkerReport, scanWorkMarkers } from '@empire-builder-kit/nx/work-markers';
9440
10306
  import { createHash } from 'node:crypto';
9441
10307
  import { existsSync, readdirSync, readFileSync } from 'node:fs';
@@ -9498,13 +10364,7 @@ if (
9498
10364
  validateOwnership('.', workspace, 'Workspace');
9499
10365
  validateOwnership('packages/foundation', foundation, 'Foundation');
9500
10366
 
9501
- const policy = validateRepositoryOperatingPolicy(readJson('ebk.repository-policy.json'));
9502
- if (!policy.valid) {
9503
- throw new Error(
9504
- 'Repository Operating Policy is invalid:\\n' +
9505
- policy.issues.map(({ message, path }) => '- ' + path + ': ' + message).join('\\n'),
9506
- );
9507
- }
10367
+ readRepositoryOperatingPolicy(process.cwd());
9508
10368
 
9509
10369
  for (const name of readdirSync('packages')) {
9510
10370
  if (name === 'foundation') continue;
@@ -9522,7 +10382,63 @@ assertWorkMarkerReport(markerReport);
9522
10382
  process.stdout.write('Empire Builder Kit workspace source health is green.\\n');
9523
10383
  `,
9524
10384
  };
9525
- Object.assign(files, SUPPLY_CHAIN_WORKSPACE_FILE_PLAN.files, (0, workspace_files_performance_cost_js_1.renderProtectedPerformanceCostWorkflow)(), (0, workspace_files_performance_cost_js_1.renderProtectedAggregateCountWorkflow)(), (0, workspace_files_performance_load_js_1.renderProtectedPerformanceLoadWorkflowPlan)('workspace').files, developerAgentFilePlan.files, (0, workspace_files_foundation_workload_roles_js_1.renderFoundationWorkloadRoleFiles)(), (0, workspace_files_workload_role_baseline_js_1.renderWorkspaceWorkloadRoleBaselineFiles)({ name }));
10385
+ Object.assign(files, SUPPLY_CHAIN_WORKSPACE_FILE_PLAN.files, developerAgentFilePlan.files, (0, workspace_files_adopter_skills_js_1.renderAdopterSkills)({ name, deploymentProfile }), (0, workspace_files_stage_authority_js_1.renderStageAuthorityWorkspaceFiles)({
10386
+ deploymentProfile,
10387
+ name,
10388
+ workspaceDeploymentId,
10389
+ }));
10390
+ Object.assign(files, (0, workspace_files_ecr_inspector_js_1.renderThinEcrInspectorWorkflow)({
10391
+ actionPins: {
10392
+ checkout: checkoutAction,
10393
+ configureAwsCredentials: configureAwsAction,
10394
+ downloadArtifact: downloadArtifactAction,
10395
+ pnpmSetup: pnpmAction,
10396
+ setupNode: setupNodeAction,
10397
+ uploadArtifact: uploadArtifactAction,
10398
+ },
10399
+ deploymentProfile,
10400
+ name,
10401
+ nodeVersion: tool_versions_js_1.WORKSPACE_NODE_VERSION,
10402
+ }), (0, workspace_files_thin_deploy_js_1.renderThinDeployWorkflowFilePlan)({
10403
+ deliveryLane,
10404
+ productionApprovalMode,
10405
+ actionPins: {
10406
+ cache: cacheAction,
10407
+ checkout: checkoutAction,
10408
+ configureAwsCredentials: configureAwsAction,
10409
+ downloadArtifact: downloadArtifactAction,
10410
+ pnpmSetup: pnpmAction,
10411
+ setupNode: setupNodeAction,
10412
+ uploadArtifact: uploadArtifactAction,
10413
+ },
10414
+ deploymentProfile,
10415
+ name,
10416
+ toolVersions: {
10417
+ node: tool_versions_js_1.WORKSPACE_NODE_VERSION,
10418
+ sst: tool_versions_js_1.WORKSPACE_SST_VERSION,
10419
+ },
10420
+ }).files, (0, workspace_files_thin_docs_js_1.renderThinLifecycleDocumentation)({
10421
+ deliveryLane,
10422
+ deploymentProfile,
10423
+ name,
10424
+ productionApprovalMode,
10425
+ }));
10426
+ const thinReleaseWorkflowPlan = (0, workspace_files_thin_release_js_1.renderThinReleaseWorkflowPlan)();
10427
+ Object.assign(files, thinReleaseWorkflowPlan.files);
10428
+ for (const obsoletePath of [
10429
+ ...thinReleaseWorkflowPlan.obsoletePaths,
10430
+ // The trunk lane has no develop branch to sync main back into; trunk
10431
+ // promotion only ever fast-forwards main from develop, so there is
10432
+ // nothing to sync back either.
10433
+ ...(trunkLane || trunkPromotion ? ['.github/workflows/sync-back.yml'] : []),
10434
+ '.github/workflows/performance-aggregate-counts.yml',
10435
+ '.github/workflows/performance-cost-actual.yml',
10436
+ '.github/workflows/performance-load.yml',
10437
+ 'packages/foundation/operations/infrastructure-change.json',
10438
+ 'tools/ebk-infrastructure-plan-summary.mjs',
10439
+ ]) {
10440
+ delete files[obsoletePath];
10441
+ }
9526
10442
  for (const [path, source] of Object.entries(files)) {
9527
10443
  if (/^\.github\/workflows\/[^/]+\.ya?ml$/u.test(path)) {
9528
10444
  files[path] = isolateOidcTokenBeforeAwsAuthority(source, configureAwsAction);