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

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 (791) hide show
  1. package/README.md +18 -0
  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.js +21 -9
  16. package/src/blueprints/manifest.d.ts +1 -1
  17. package/src/blueprints/resolver.js +28 -15
  18. package/src/cli/bootstrap.d.ts +1 -1
  19. package/src/cli/deploy-runtime.d.ts +6 -0
  20. package/src/cli/deploy-runtime.js +58 -0
  21. package/src/cli/dev-command.d.ts +16 -4
  22. package/src/cli/dev-command.js +63 -0
  23. package/src/cli/main.d.ts +9 -1
  24. package/src/cli/main.js +139 -20
  25. package/src/cli/output.d.ts +1 -1
  26. package/src/cli/output.js +1 -1
  27. package/src/developer-agent/context.js +41 -9
  28. package/src/developer-agent/generator-planning.js +20 -6
  29. package/src/developer-agent/git-inspection.d.ts +6 -2
  30. package/src/developer-agent/git-inspection.js +78 -6
  31. package/src/developer-agent/model.d.ts +5 -3
  32. package/src/developer-agent/model.js +4 -4
  33. package/src/developer-agent/operation-registry.d.ts +5 -0
  34. package/src/developer-agent/operation-registry.js +286 -40
  35. package/src/developer-agent/planning.js +16 -13
  36. package/src/developer-agent/preview-changes.d.ts +2 -0
  37. package/src/developer-agent/preview-changes.js +14 -2
  38. package/src/developer-agent/preview-source.js +3 -0
  39. package/src/developer-agent/validation.js +25 -9
  40. package/src/developer-agent/workspace-inspection.js +60 -5
  41. package/src/executors/blueprint-trust/blueprint-trust.js +6 -0
  42. package/src/executors/deployment-plan/deployment-plan.d.ts +2 -0
  43. package/src/executors/deployment-plan/deployment-plan.js +57 -2
  44. package/src/executors/dev-doctor/dev-doctor.js +13 -6
  45. package/src/executors/dev-task-cleanup/dev-task-cleanup.d.ts +23 -0
  46. package/src/executors/dev-task-cleanup/dev-task-cleanup.js +145 -0
  47. package/src/executors/dev-task-cleanup/schema.json +9 -0
  48. package/src/executors/dev-task-cleanup/task-stack-absence.d.ts +27 -0
  49. package/src/executors/dev-task-cleanup/task-stack-absence.js +281 -0
  50. package/src/executors/dev-task-cleanup/task-stack-checkpoint.d.ts +12 -0
  51. package/src/executors/dev-task-cleanup/task-stack-checkpoint.js +92 -0
  52. package/src/executors/fleet/fleet.js +1 -1
  53. package/src/executors/foundation-preflight/foundation-preflight.d.ts +1 -0
  54. package/src/executors/foundation-preflight/foundation-preflight.js +6 -2
  55. package/src/executors/foundation-preflight/schema.json +5 -0
  56. package/src/executors/foundation-profile-preflight/foundation-profile-preflight.d.ts +13 -2
  57. package/src/executors/foundation-profile-preflight/foundation-profile-preflight.js +95 -31
  58. package/src/executors/foundation-profile-preflight/production-database-safeguard.d.ts +65 -0
  59. package/src/executors/foundation-profile-preflight/production-database-safeguard.js +223 -0
  60. package/src/executors/foundation-profile-preflight/schema.json +15 -4
  61. package/src/executors/foundation-remove-preflight/foundation-remove-preflight.d.ts +11 -1
  62. package/src/executors/foundation-remove-preflight/foundation-remove-preflight.js +70 -20
  63. package/src/executors/foundation-remove-preflight/schema.json +6 -1
  64. package/src/executors/github-bootstrap/github-bootstrap.d.ts +1 -0
  65. package/src/executors/github-bootstrap/github-bootstrap.js +3 -0
  66. package/src/executors/github-bootstrap/schema.json +7 -1
  67. package/src/executors/infrastructure-plan/infrastructure-plan.d.ts +29 -136
  68. package/src/executors/infrastructure-plan/infrastructure-plan.js +230 -3282
  69. package/src/executors/infrastructure-plan/schema.json +12 -52
  70. package/src/executors/local-deploy/federated-credential-transport.d.ts +48 -0
  71. package/src/executors/local-deploy/federated-credential-transport.js +252 -0
  72. package/src/executors/local-deploy/local-approval.d.ts +18 -0
  73. package/src/executors/local-deploy/local-approval.js +39 -0
  74. package/src/executors/local-deploy/local-deploy.d.ts +112 -0
  75. package/src/executors/local-deploy/local-deploy.js +1404 -0
  76. package/src/executors/local-deploy/schema.json +70 -0
  77. package/src/executors/refresh/refresh.js +2 -2
  78. package/src/executors/remove/remove.js +2 -2
  79. package/src/executors/router-waf-rehearsal/router-waf-rehearsal.d.ts +42 -0
  80. package/src/executors/router-waf-rehearsal/router-waf-rehearsal.js +1229 -0
  81. package/src/executors/router-waf-rehearsal/schema.json +29 -0
  82. package/src/executors/secret-initialize/schema.json +42 -0
  83. package/src/executors/secret-initialize/secret-initialize.d.ts +34 -0
  84. package/src/executors/secret-initialize/secret-initialize.js +297 -0
  85. package/src/executors/secret-metadata/schema.json +5 -0
  86. package/src/executors/secret-metadata/secret-metadata.d.ts +16 -2
  87. package/src/executors/secret-metadata/secret-metadata.js +129 -30
  88. package/src/executors/sst-lifecycle/generated-secret-retirement.d.ts +53 -0
  89. package/src/executors/sst-lifecycle/generated-secret-retirement.js +192 -0
  90. package/src/executors/sst-lifecycle/governed-native-operation.d.ts +48 -0
  91. package/src/executors/sst-lifecycle/governed-native-operation.js +162 -0
  92. package/src/executors/sst-lifecycle/logging-preflight.d.ts +5 -0
  93. package/src/executors/sst-lifecycle/logging-preflight.js +23 -13
  94. package/src/executors/sst-lifecycle/run-sst-command.d.ts +40 -6
  95. package/src/executors/sst-lifecycle/run-sst-command.js +779 -70
  96. package/src/executors/sst-lifecycle/schema.json +8 -1
  97. package/src/executors/sst-lifecycle/secret-preflight.d.ts +2 -0
  98. package/src/executors/sst-lifecycle/secret-preflight.js +13 -7
  99. package/src/executors/sst-lifecycle/sst-environment-source.d.ts +33 -0
  100. package/src/executors/sst-lifecycle/sst-environment-source.js +267 -0
  101. package/src/executors/sst-lifecycle/task-unlock.d.ts +15 -0
  102. package/src/executors/sst-lifecycle/task-unlock.js +126 -0
  103. package/src/executors/unlock/unlock.js +2 -2
  104. package/src/foundation/capability-plan.d.ts +43 -0
  105. package/src/foundation/capability-plan.js +168 -0
  106. package/src/foundation/discovery.d.ts +14 -11
  107. package/src/foundation/discovery.js +32 -6
  108. package/src/foundation/index.d.ts +4 -0
  109. package/src/foundation/index.js +11 -0
  110. package/src/foundation/local-database-contract.d.ts +20 -0
  111. package/src/foundation/local-database-contract.js +262 -13
  112. package/src/foundation/local-private-smoke-access.d.ts +20 -0
  113. package/src/foundation/local-private-smoke-access.js +197 -0
  114. package/src/foundation/logging-preflight.js +1 -1
  115. package/src/foundation/logging.d.ts +3 -1
  116. package/src/foundation/logging.js +1 -1
  117. package/src/foundation/partial-state.d.ts +10 -0
  118. package/src/foundation/partial-state.js +21 -0
  119. package/src/foundation/preflight.js +37 -18
  120. package/src/foundation/private-smoke-access.d.ts +25 -0
  121. package/src/foundation/private-smoke-access.js +135 -0
  122. package/src/foundation/private-smoke-admission.d.ts +45 -0
  123. package/src/foundation/private-smoke-admission.js +180 -0
  124. package/src/foundation/private-smoke-control-plane.d.ts +18 -0
  125. package/src/foundation/private-smoke-control-plane.js +184 -0
  126. package/src/foundation/provision-database-bindings.d.ts +5 -0
  127. package/src/foundation/provision-database-bindings.js +41 -8
  128. package/src/foundation/provision-logical-database.js +15 -8
  129. package/src/foundation/requirements.d.ts +3 -3
  130. package/src/foundation/requirements.js +24 -12
  131. package/src/foundation/router-waf-rate-rehearsal.d.ts +97 -0
  132. package/src/foundation/router-waf-rate-rehearsal.js +210 -0
  133. package/src/foundation/router-waf.d.ts +149 -0
  134. package/src/foundation/router-waf.js +809 -0
  135. package/src/foundation/stages.d.ts +1 -0
  136. package/src/foundation/stages.js +11 -0
  137. package/src/generators/blueprint/blueprint.d.ts +5 -0
  138. package/src/generators/blueprint/blueprint.js +102 -0
  139. package/src/generators/blueprint/files.d.ts +6 -0
  140. package/src/generators/blueprint/files.js +217 -0
  141. package/src/generators/blueprint/schema.d.ts +4 -0
  142. package/src/generators/blueprint/schema.json +18 -0
  143. package/src/generators/formatting.d.ts +8 -0
  144. package/src/generators/formatting.js +141 -0
  145. package/src/generators/function/function.js +38 -17
  146. package/src/generators/logging-registry.d.ts +2 -0
  147. package/src/generators/logging-registry.js +22 -0
  148. package/src/generators/post-generation-install.js +2 -2
  149. package/src/generators/preset/dependency-overrides.d.ts +8 -0
  150. package/src/generators/preset/dependency-overrides.js +55 -0
  151. package/src/generators/preset/generator.js +43 -3
  152. package/src/generators/preset/schema.d.ts +10 -0
  153. package/src/generators/preset/schema.json +47 -0
  154. package/src/generators/preset/tool-versions.d.ts +2 -0
  155. package/src/generators/preset/tool-versions.js +3 -1
  156. package/src/generators/preset/workspace-files-adopter-skills.d.ts +7 -0
  157. package/src/generators/preset/workspace-files-adopter-skills.js +303 -0
  158. package/src/generators/preset/workspace-files-ci-acceptance.d.ts +1 -0
  159. package/src/generators/preset/workspace-files-ci-acceptance.js +791 -0
  160. package/src/generators/preset/workspace-files-ci-install.d.ts +2 -0
  161. package/src/generators/preset/workspace-files-ci-install.js +196 -0
  162. package/src/generators/preset/workspace-files-developer-agent.d.ts +2 -0
  163. package/src/generators/preset/workspace-files-developer-agent.js +90 -28
  164. package/src/generators/preset/workspace-files-ecr-inspector.d.ts +18 -0
  165. package/src/generators/preset/workspace-files-ecr-inspector.js +298 -0
  166. package/src/generators/preset/workspace-files-foundation-workload-roles.js +5 -2
  167. package/src/generators/preset/workspace-files-performance-cost.d.ts +2 -2
  168. package/src/generators/preset/workspace-files-performance-cost.js +8 -8
  169. package/src/generators/preset/workspace-files-performance-load.d.ts +1 -1
  170. package/src/generators/preset/workspace-files-performance-load.js +39 -25
  171. package/src/generators/preset/workspace-files-stage-authority.d.ts +6 -0
  172. package/src/generators/preset/workspace-files-stage-authority.js +518 -0
  173. package/src/generators/preset/workspace-files-thin-deploy.d.ts +55 -0
  174. package/src/generators/preset/workspace-files-thin-deploy.js +2473 -0
  175. package/src/generators/preset/workspace-files-thin-docs.d.ts +7 -0
  176. package/src/generators/preset/workspace-files-thin-docs.js +352 -0
  177. package/src/generators/preset/workspace-files-thin-release.d.ts +14 -0
  178. package/src/generators/preset/workspace-files-thin-release.js +26 -0
  179. package/src/generators/preset/workspace-files-workload-role-baseline.js +254 -42
  180. package/src/generators/preset/workspace-files.d.ts +18 -2
  181. package/src/generators/preset/workspace-files.js +1504 -608
  182. package/src/generators/repository-policy/repository-policy.js +235 -5
  183. package/src/generators/repository-policy/schema.d.ts +10 -0
  184. package/src/generators/repository-policy/schema.json +80 -4
  185. package/src/generators/repository-policy/tree-policy.d.ts +3 -0
  186. package/src/generators/repository-policy/tree-policy.js +25 -0
  187. package/src/generators/slice/built-in-plan-api-contract-files.js +42 -7
  188. package/src/generators/slice/built-in-plan-api-files.d.ts +1 -1
  189. package/src/generators/slice/built-in-plan-api-files.js +130 -414
  190. package/src/generators/slice/built-in-plan-async-work-files.js +166 -1969
  191. package/src/generators/slice/built-in-plan-authorization-files.js +92 -329
  192. package/src/generators/slice/built-in-plan-authorization-infrastructure-files.js +60 -2
  193. package/src/generators/slice/built-in-plan-authorization-storage.d.ts +3 -0
  194. package/src/generators/slice/built-in-plan-authorization-storage.js +119 -0
  195. package/src/generators/slice/built-in-plan-background-work-files.d.ts +2 -0
  196. package/src/generators/slice/built-in-plan-background-work-files.js +99 -0
  197. package/src/generators/slice/built-in-plan-data-deletion-files.js +2 -830
  198. package/src/generators/slice/built-in-plan-data-export-infrastructure-files.js +5 -31
  199. package/src/generators/slice/built-in-plan-data-files.d.ts +1 -1
  200. package/src/generators/slice/built-in-plan-data-files.js +163 -1196
  201. package/src/generators/slice/built-in-plan-data-lifecycle-files.js +53 -28
  202. package/src/generators/slice/built-in-plan-data-lifecycle-runtime-files.js +37 -2
  203. package/src/generators/slice/built-in-plan-data-retention-job-files.js +7 -2443
  204. package/src/generators/slice/built-in-plan-data-retention-launcher-files.js +50 -8
  205. package/src/generators/slice/built-in-plan-data-rights-files.js +6 -4482
  206. package/src/generators/slice/built-in-plan-database-lifecycle-files.js +44 -25
  207. package/src/generators/slice/built-in-plan-database-retirement-rehearsal-files.js +20 -1
  208. package/src/generators/slice/built-in-plan-foundation-files.d.ts +2 -1
  209. package/src/generators/slice/built-in-plan-foundation-files.js +39 -18
  210. package/src/generators/slice/built-in-plan-frontend-files.js +7 -3
  211. package/src/generators/slice/built-in-plan-infrastructure-files.d.ts +1 -1
  212. package/src/generators/slice/built-in-plan-infrastructure-files.js +933 -729
  213. package/src/generators/slice/built-in-plan-infrastructure-safety-files.js +67 -11
  214. package/src/generators/slice/built-in-plan-key-value-cleanup.d.ts +4 -0
  215. package/src/generators/slice/built-in-plan-key-value-cleanup.js +100 -0
  216. package/src/generators/slice/built-in-plan-key-value-consumer.d.ts +4 -0
  217. package/src/generators/slice/built-in-plan-key-value-consumer.js +151 -0
  218. package/src/generators/slice/built-in-plan-key-value-deletion.d.ts +2 -0
  219. package/src/generators/slice/built-in-plan-key-value-deletion.js +377 -0
  220. package/src/generators/slice/built-in-plan-key-value-delivery.d.ts +2 -0
  221. package/src/generators/slice/built-in-plan-key-value-delivery.js +137 -0
  222. package/src/generators/slice/built-in-plan-key-value-export.d.ts +2 -0
  223. package/src/generators/slice/built-in-plan-key-value-export.js +595 -0
  224. package/src/generators/slice/built-in-plan-key-value-files.d.ts +2 -0
  225. package/src/generators/slice/built-in-plan-key-value-files.js +31 -0
  226. package/src/generators/slice/built-in-plan-key-value-holds.d.ts +2 -0
  227. package/src/generators/slice/built-in-plan-key-value-holds.js +189 -0
  228. package/src/generators/slice/built-in-plan-key-value-infrastructure.d.ts +2 -0
  229. package/src/generators/slice/built-in-plan-key-value-infrastructure.js +102 -0
  230. package/src/generators/slice/built-in-plan-key-value-performance.d.ts +5 -0
  231. package/src/generators/slice/built-in-plan-key-value-performance.js +530 -0
  232. package/src/generators/slice/built-in-plan-key-value-provider.d.ts +4 -0
  233. package/src/generators/slice/built-in-plan-key-value-provider.js +366 -0
  234. package/src/generators/slice/built-in-plan-key-value-records.d.ts +3 -0
  235. package/src/generators/slice/built-in-plan-key-value-records.js +232 -0
  236. package/src/generators/slice/built-in-plan-key-value-retention-infrastructure.d.ts +2 -0
  237. package/src/generators/slice/built-in-plan-key-value-retention-infrastructure.js +103 -0
  238. package/src/generators/slice/built-in-plan-key-value-retention-job-files.d.ts +2 -0
  239. package/src/generators/slice/built-in-plan-key-value-retention-job-files.js +160 -0
  240. package/src/generators/slice/built-in-plan-key-value-retention-maintenance.d.ts +2 -0
  241. package/src/generators/slice/built-in-plan-key-value-retention-maintenance.js +329 -0
  242. package/src/generators/slice/built-in-plan-key-value-retention-worker.d.ts +2 -0
  243. package/src/generators/slice/built-in-plan-key-value-retention-worker.js +477 -0
  244. package/src/generators/slice/built-in-plan-key-value-retention.d.ts +2 -0
  245. package/src/generators/slice/built-in-plan-key-value-retention.js +184 -0
  246. package/src/generators/slice/built-in-plan-metadata-files.d.ts +1 -1
  247. package/src/generators/slice/built-in-plan-metadata-files.js +157 -28
  248. package/src/generators/slice/built-in-plan-native-runtime-authority-files.d.ts +2 -0
  249. package/src/generators/slice/built-in-plan-native-runtime-authority-files.js +324 -0
  250. package/src/generators/slice/built-in-plan-operations-files.d.ts +1 -1
  251. package/src/generators/slice/built-in-plan-operations-files.js +585 -97
  252. package/src/generators/slice/built-in-plan-performance-files.d.ts +2 -1
  253. package/src/generators/slice/built-in-plan-performance-files.js +220 -1074
  254. package/src/generators/slice/built-in-plan-project-files.js +141 -135
  255. package/src/generators/slice/built-in-plan-release-files.d.ts +1 -1
  256. package/src/generators/slice/built-in-plan-release-files.js +310 -70
  257. package/src/generators/slice/built-in-plan-reliability-files.d.ts +1 -1
  258. package/src/generators/slice/built-in-plan-reliability-files.js +279 -43
  259. package/src/generators/slice/built-in-plan-secret-runtime-files.d.ts +1 -1
  260. package/src/generators/slice/built-in-plan-secret-runtime-files.js +87 -76
  261. package/src/generators/slice/built-in-plan-shared.d.ts +4 -2
  262. package/src/generators/slice/built-in-plan-shared.js +41 -19
  263. package/src/generators/slice/built-in-plan-sql-events.d.ts +2 -0
  264. package/src/generators/slice/built-in-plan-sql-events.js +276 -0
  265. package/src/generators/slice/built-in-plan-testing-files.d.ts +1 -1
  266. package/src/generators/slice/built-in-plan-testing-files.js +304 -172
  267. package/src/generators/slice/built-in-plan-testing-persistence-files.d.ts +2 -0
  268. package/src/generators/slice/built-in-plan-testing-persistence-files.js +178 -0
  269. package/src/generators/slice/built-in-plan-thin-release-files.d.ts +11 -0
  270. package/src/generators/slice/built-in-plan-thin-release-files.js +162 -0
  271. package/src/generators/slice/built-in-plan-verification-files.js +76 -2
  272. package/src/generators/slice/built-in-plan-web-files.d.ts +1 -1
  273. package/src/generators/slice/built-in-plan-web-files.js +223 -16
  274. package/src/generators/slice/built-in-plan-workload-role-files.d.ts +1 -1
  275. package/src/generators/slice/built-in-plan-workload-role-files.js +143 -279
  276. package/src/generators/slice/built-in-plan.d.ts +11 -1
  277. package/src/generators/slice/built-in-plan.js +80 -29
  278. package/src/generators/slice/data-policy-review-seed.d.ts +2 -0
  279. package/src/generators/slice/data-policy-review-seed.js +22 -0
  280. package/src/generators/slice/deployed-smoke-readiness.d.ts +2 -0
  281. package/src/generators/slice/deployed-smoke-readiness.js +214 -0
  282. package/src/generators/slice/local-source-identity.d.ts +2 -0
  283. package/src/generators/slice/local-source-identity.js +31 -0
  284. package/src/generators/slice/persistence-contract.d.ts +14 -0
  285. package/src/generators/slice/persistence-contract.js +32 -0
  286. package/src/generators/slice/slice.js +61 -50
  287. package/src/generators/sql/schema.d.ts +7 -0
  288. package/src/generators/sql/schema.json +38 -0
  289. package/src/generators/sql/sql.d.ts +6 -0
  290. package/src/generators/sql/sql.js +53 -0
  291. package/src/generators/upgrade/sql-seeded-reconciliation.d.ts +2 -0
  292. package/src/generators/upgrade/sql-seeded-reconciliation.js +170 -0
  293. package/src/generators/upgrade/upgrade.d.ts +15 -1
  294. package/src/generators/upgrade/upgrade.js +435 -44
  295. package/src/generators/workload/performance-registration.d.ts +1 -0
  296. package/src/generators/workload/performance-registration.js +9 -11
  297. package/src/generators/workload/service-performance-files.d.ts +1 -0
  298. package/src/generators/workload/service-performance-files.js +27 -7
  299. package/src/generators/workload/workload.d.ts +1 -1
  300. package/src/generators/workload/workload.js +105 -29
  301. package/src/git-policy/action-pins.d.ts +5 -0
  302. package/src/git-policy/action-pins.js +5 -0
  303. package/src/git-policy/github-bootstrap.d.ts +39 -8
  304. package/src/git-policy/github-bootstrap.js +329 -175
  305. package/src/git-policy/routing.d.ts +7 -1
  306. package/src/git-policy/routing.js +62 -1
  307. package/src/index.d.ts +1 -0
  308. package/src/index.js +1 -0
  309. package/src/infrastructure-change/local-capability.d.ts +73 -0
  310. package/src/infrastructure-change/local-capability.js +114 -0
  311. package/src/infrastructure-change/model.d.ts +25 -4
  312. package/src/infrastructure-change/model.js +1 -0
  313. package/src/infrastructure-change/normalize.d.ts +4 -0
  314. package/src/infrastructure-change/normalize.js +47 -7
  315. package/src/infrastructure-change/plan.d.ts +30 -3
  316. package/src/infrastructure-change/plan.js +323 -13
  317. package/src/infrastructure-change/risk.d.ts +3 -1
  318. package/src/infrastructure-change/risk.js +96 -16
  319. package/src/local-dev/browser-verification.d.ts +37 -0
  320. package/src/local-dev/browser-verification.js +281 -0
  321. package/src/local-dev/contracts.d.ts +10 -1
  322. package/src/local-dev/contracts.js +114 -13
  323. package/src/local-dev/dev-command-core.js +24 -5
  324. package/src/local-dev/docker-credential-health.d.ts +14 -0
  325. package/src/local-dev/docker-credential-health.js +94 -0
  326. package/src/local-dev/environment-inspector.d.ts +7 -0
  327. package/src/local-dev/environment-inspector.js +42 -15
  328. package/src/local-dev/environment-state.js +2 -2
  329. package/src/local-dev/errors.d.ts +1 -1
  330. package/src/local-dev/errors.js +2 -0
  331. package/src/local-dev/fleet-client.js +2 -2
  332. package/src/local-dev/fleet-configuration.js +57 -25
  333. package/src/local-dev/fleet-inventory.js +46 -18
  334. package/src/local-dev/fleet-ipc.d.ts +7 -0
  335. package/src/local-dev/fleet-ipc.js +14 -2
  336. package/src/local-dev/fleet-launcher.d.ts +6 -0
  337. package/src/local-dev/fleet-launcher.js +14 -2
  338. package/src/local-dev/fleet-logs.js +50 -22
  339. package/src/local-dev/fleet-process.d.ts +20 -1
  340. package/src/local-dev/fleet-process.js +197 -41
  341. package/src/local-dev/fleet-runner-evidence.d.ts +5 -0
  342. package/src/local-dev/fleet-runner-evidence.js +56 -0
  343. package/src/local-dev/fleet-runtime.js +268 -229
  344. package/src/local-dev/fleet-session.d.ts +2 -0
  345. package/src/local-dev/fleet-session.js +28 -11
  346. package/src/local-dev/fleet-slice-runner-daemon.js +4 -0
  347. package/src/local-dev/fleet-state.d.ts +5 -2
  348. package/src/local-dev/fleet-state.js +25 -4
  349. package/src/local-dev/fleet-supervisor.d.ts +3 -0
  350. package/src/local-dev/fleet-supervisor.js +66 -11
  351. package/src/local-dev/fleet-web-start.d.ts +12 -0
  352. package/src/local-dev/fleet-web-start.js +48 -0
  353. package/src/local-dev/identity.js +3 -14
  354. package/src/local-dev/index.d.ts +1 -0
  355. package/src/local-dev/index.js +1 -0
  356. package/src/local-dev/native-completion.d.ts +9 -0
  357. package/src/local-dev/native-completion.js +130 -0
  358. package/src/local-dev/nx-workspace.d.ts +4 -2
  359. package/src/local-dev/nx-workspace.js +11 -6
  360. package/src/local-dev/personal-stage.d.ts +4 -5
  361. package/src/local-dev/personal-stage.js +6 -54
  362. package/src/local-dev/port-claims.d.ts +13 -0
  363. package/src/local-dev/port-claims.js +46 -0
  364. package/src/local-dev/ports.d.ts +14 -2
  365. package/src/local-dev/ports.js +78 -3
  366. package/src/local-dev/private-state.d.ts +11 -0
  367. package/src/local-dev/private-state.js +146 -40
  368. package/src/local-dev/process-group.d.ts +4 -0
  369. package/src/local-dev/process-group.js +29 -0
  370. package/src/local-dev/process-identity.d.ts +8 -0
  371. package/src/local-dev/process-identity.js +31 -1
  372. package/src/local-dev/proxy.js +11 -12
  373. package/src/local-dev/runtime-paths.js +1 -1
  374. package/src/local-dev/slice-claim.js +1 -1
  375. package/src/local-dev/slice-runner.d.ts +15 -6
  376. package/src/local-dev/slice-runner.js +189 -54
  377. package/src/local-dev/task-attachment.d.ts +17 -0
  378. package/src/local-dev/task-attachment.js +73 -0
  379. package/src/local-dev/task-authority.d.ts +10 -0
  380. package/src/local-dev/task-authority.js +27 -0
  381. package/src/local-dev/task-identity.d.ts +33 -0
  382. package/src/local-dev/task-identity.js +418 -0
  383. package/src/local-dev/task-native-writer.d.ts +18 -0
  384. package/src/local-dev/task-native-writer.js +329 -0
  385. package/src/local-dev/tls.js +8 -2
  386. package/src/local-dev/web-launch.d.ts +9 -0
  387. package/src/local-dev/web-launch.js +130 -0
  388. package/src/ownership/composition.d.ts +2 -1
  389. package/src/ownership/composition.js +1 -0
  390. package/src/performance/cost-collection.d.ts +1 -1
  391. package/src/performance/cost-collection.js +4 -2
  392. package/src/performance/cost-controls.d.ts +8 -4
  393. package/src/performance/cost-controls.js +39 -28
  394. package/src/performance/evidence.d.ts +1 -1
  395. package/src/performance/evidence.js +5 -3
  396. package/src/performance/load-safety.js +5 -3
  397. package/src/performance/model.d.ts +4 -4
  398. package/src/performance/model.js +4 -5
  399. package/src/performance/scenario.d.ts +22 -0
  400. package/src/performance/scenario.js +100 -92
  401. package/src/release/index.d.ts +0 -2
  402. package/src/release/index.js +0 -2
  403. package/src/release/source-authority.d.ts +5 -5
  404. package/src/release/source-authority.js +141 -124
  405. package/src/repository-policy/aurora-preflight.d.ts +1 -1
  406. package/src/repository-policy/aurora-preflight.js +4 -1
  407. package/src/repository-policy/capacity-migration.d.ts +3 -0
  408. package/src/repository-policy/capacity-migration.js +21 -0
  409. package/src/repository-policy/deployment-plan.d.ts +4 -1
  410. package/src/repository-policy/deployment-plan.js +9 -0
  411. package/src/repository-policy/foundation-profile.js +10 -1
  412. package/src/repository-policy/policy.d.ts +57 -3
  413. package/src/repository-policy/policy.js +497 -11
  414. package/src/schemas/blueprint-v1.json +5 -4
  415. package/src/schemas/foundation-requirement-v1.json +5 -4
  416. package/src/schemas/performance-policy-v1.json +3 -3
  417. package/src/schemas/repository-policy-v1.json +130 -6
  418. package/src/schemas/testing-evidence-result-v1.json +1 -1
  419. package/src/secrets/adapters.d.ts +7 -2
  420. package/src/secrets/adapters.js +34 -3
  421. package/src/secrets/aws-control-plane.d.ts +10 -1
  422. package/src/secrets/aws-control-plane.js +67 -0
  423. package/src/secrets/generated-aws-control-plane.d.ts +23 -0
  424. package/src/secrets/generated-aws-control-plane.js +259 -0
  425. package/src/secrets/metadata.js +38 -10
  426. package/src/secrets/model.d.ts +8 -1
  427. package/src/secrets/model.js +24 -3
  428. package/src/secrets/operator.d.ts +91 -3
  429. package/src/secrets/operator.js +463 -13
  430. package/src/secrets/registry.d.ts +2 -1
  431. package/src/secrets/registry.js +18 -1
  432. package/src/secrets/verification.js +9 -1
  433. package/src/secrets/workload-access.js +10 -2
  434. package/src/sst-environment-source-format.d.ts +6 -0
  435. package/src/sst-environment-source-format.js +199 -0
  436. package/src/supply-chain/action-pins.js +61 -7
  437. package/src/supply-chain/blueprint-trust-git.js +85 -24
  438. package/src/supply-chain/blueprint-verification-authority.js +55 -3
  439. package/src/supply-chain/build-output-bounds.d.ts +2 -0
  440. package/src/supply-chain/build-output-bounds.js +12 -0
  441. package/src/supply-chain/build-output.d.ts +7 -1
  442. package/src/supply-chain/build-output.js +177 -24
  443. package/src/supply-chain/ecr-scanning.d.ts +5 -3
  444. package/src/supply-chain/ecr-scanning.js +11 -7
  445. package/src/supply-chain/ecs-deployed-image.d.ts +5 -3
  446. package/src/supply-chain/ecs-deployed-image.js +6 -4
  447. package/src/supply-chain/generated-blueprint-trust-command.js +18 -1
  448. package/src/supply-chain/generated-command.js +1 -0
  449. package/src/supply-chain/generated-container-release-command.js +1285 -124
  450. package/src/supply-chain/generated-ecr-collector-command.d.ts +0 -1
  451. package/src/supply-chain/generated-ecr-collector-command.js +200 -93
  452. package/src/supply-chain/generated-files.js +25 -14
  453. package/src/supply-chain/generated-scanner-command.js +2 -1
  454. package/src/supply-chain/index.d.ts +1 -1
  455. package/src/supply-chain/index.js +2 -1
  456. package/src/supply-chain/sbom.d.ts +1 -1
  457. package/src/supply-chain/sbom.js +30 -5
  458. package/src/supply-chain/scanner.js +3 -0
  459. package/src/testing-evidence/fixture-scope.d.ts +2 -1
  460. package/src/testing-evidence/fixture-scope.js +4 -2
  461. package/src/testing-evidence/result.d.ts +2 -1
  462. package/src/testing-evidence/result.js +18 -4
  463. package/src/workload-roles/account-baseline.d.ts +11 -0
  464. package/src/workload-roles/account-baseline.js +253 -16
  465. package/src/workload-roles/authority.d.ts +3 -1
  466. package/src/workload-roles/authority.js +48 -2
  467. package/src/workload-roles/aws-s3-state.js +27 -0
  468. package/src/workload-roles/boundary.d.ts +2 -1
  469. package/src/workload-roles/boundary.js +9 -2
  470. package/src/workload-roles/handler.js +52 -24
  471. package/src/workload-roles/index.d.ts +10 -17
  472. package/src/workload-roles/index.js +5 -18
  473. package/src/workload-roles/injection.js +1 -1
  474. package/src/workload-roles/provisioner.js +12 -4
  475. package/src/workload-roles/registration.d.ts +1 -0
  476. package/src/workload-roles/registration.js +5 -0
  477. package/src/workload-roles/stage-boundary.js +1 -1
  478. package/src/workload-roles/standard-slice.js +3 -3
  479. package/src/workspace-deployment/affected-lifecycle.d.ts +53 -0
  480. package/src/workspace-deployment/affected-lifecycle.js +84 -0
  481. package/src/workspace-deployment/identity.d.ts +3 -0
  482. package/src/workspace-deployment/identity.js +22 -0
  483. package/src/workspace-deployment/index.d.ts +4 -0
  484. package/src/workspace-deployment/index.js +8 -0
  485. package/src/workspace-deployment/migration-evidence.d.ts +29 -0
  486. package/src/workspace-deployment/migration-evidence.js +83 -0
  487. package/src/workspace-deployment/migration.d.ts +27 -0
  488. package/src/workspace-deployment/migration.js +109 -0
  489. package/src/workspace-deployment/ownership.d.ts +81 -0
  490. package/src/workspace-deployment/ownership.js +381 -0
  491. package/src/api-contract/baseline-update.js.map +0 -1
  492. package/src/api-contract/compatibility.js.map +0 -1
  493. package/src/api-contract/contract-graph.js.map +0 -1
  494. package/src/api-contract/evidence.js.map +0 -1
  495. package/src/api-contract/index.js.map +0 -1
  496. package/src/api-contract/model.fixtures.d.ts +0 -2
  497. package/src/api-contract/model.fixtures.js +0 -81
  498. package/src/api-contract/model.fixtures.js.map +0 -1
  499. package/src/api-contract/model.js.map +0 -1
  500. package/src/api-contract/rollout.js.map +0 -1
  501. package/src/authorization/compatibility.js.map +0 -1
  502. package/src/authorization/index.js.map +0 -1
  503. package/src/blueprints/execution.js.map +0 -1
  504. package/src/blueprints/external-sandbox.js.map +0 -1
  505. package/src/blueprints/index.js.map +0 -1
  506. package/src/blueprints/inputs.js.map +0 -1
  507. package/src/blueprints/manifest.js.map +0 -1
  508. package/src/blueprints/resolver.js.map +0 -1
  509. package/src/blueprints/transitions.js.map +0 -1
  510. package/src/cli/agent-command.js.map +0 -1
  511. package/src/cli/agent-runtime.js.map +0 -1
  512. package/src/cli/blueprint-authority.js.map +0 -1
  513. package/src/cli/blueprint-command.js.map +0 -1
  514. package/src/cli/bootstrap.js.map +0 -1
  515. package/src/cli/dev-command.js.map +0 -1
  516. package/src/cli/main.js.map +0 -1
  517. package/src/cli/output.js.map +0 -1
  518. package/src/cli/personal-secret-runtime.js.map +0 -1
  519. package/src/cli/secret-terminal.js.map +0 -1
  520. package/src/container-images.js.map +0 -1
  521. package/src/data-lifecycle/index.js.map +0 -1
  522. package/src/data-lifecycle/model.fixtures.d.ts +0 -2
  523. package/src/data-lifecycle/model.fixtures.js +0 -244
  524. package/src/data-lifecycle/model.fixtures.js.map +0 -1
  525. package/src/data-lifecycle/model.js.map +0 -1
  526. package/src/data-lifecycle/sensitive-artifacts.js.map +0 -1
  527. package/src/data-lifecycle/validation.js.map +0 -1
  528. package/src/database-retirement/contract.js.map +0 -1
  529. package/src/database-retirement/index.js.map +0 -1
  530. package/src/deterministic-order.js.map +0 -1
  531. package/src/developer-agent/context.js.map +0 -1
  532. package/src/developer-agent/generator-planning.js.map +0 -1
  533. package/src/developer-agent/generator-preview.js.map +0 -1
  534. package/src/developer-agent/git-inspection.js.map +0 -1
  535. package/src/developer-agent/handoff.js.map +0 -1
  536. package/src/developer-agent/index.js.map +0 -1
  537. package/src/developer-agent/model.js.map +0 -1
  538. package/src/developer-agent/operation-registry.js.map +0 -1
  539. package/src/developer-agent/ordering.js.map +0 -1
  540. package/src/developer-agent/planning.js.map +0 -1
  541. package/src/developer-agent/preview-changes.js.map +0 -1
  542. package/src/developer-agent/preview-source.js.map +0 -1
  543. package/src/developer-agent/validation.js.map +0 -1
  544. package/src/developer-agent/workspace-inspection.js.map +0 -1
  545. package/src/developer-agent/workspace-path.js.map +0 -1
  546. package/src/executors/blueprint-trust/blueprint-trust.js.map +0 -1
  547. package/src/executors/blueprint-verification/blueprint-verification.js.map +0 -1
  548. package/src/executors/deploy/deploy.js.map +0 -1
  549. package/src/executors/deployment-plan/deployment-plan.js.map +0 -1
  550. package/src/executors/dev/dev.js.map +0 -1
  551. package/src/executors/dev-doctor/dev-doctor.js.map +0 -1
  552. package/src/executors/dev-setup/dev-setup.js.map +0 -1
  553. package/src/executors/developer-context/developer-context.js.map +0 -1
  554. package/src/executors/fleet/fleet.js.map +0 -1
  555. package/src/executors/foundation-preflight/foundation-preflight.js.map +0 -1
  556. package/src/executors/foundation-profile-preflight/foundation-profile-preflight.js.map +0 -1
  557. package/src/executors/foundation-remove-preflight/foundation-remove-preflight.js.map +0 -1
  558. package/src/executors/github-bootstrap/github-bootstrap.js.map +0 -1
  559. package/src/executors/infrastructure-plan/infrastructure-plan.js.map +0 -1
  560. package/src/executors/refresh/refresh.js.map +0 -1
  561. package/src/executors/remove/remove.js.map +0 -1
  562. package/src/executors/secret-guardrails/secret-guardrails.js.map +0 -1
  563. package/src/executors/secret-metadata/secret-metadata.js.map +0 -1
  564. package/src/executors/sst-lifecycle/logging-preflight.js.map +0 -1
  565. package/src/executors/sst-lifecycle/run-sst-command.js.map +0 -1
  566. package/src/executors/sst-lifecycle/secret-preflight.js.map +0 -1
  567. package/src/executors/unlock/unlock.js.map +0 -1
  568. package/src/executors/work-markers/work-markers.js.map +0 -1
  569. package/src/foundation/discovery.js.map +0 -1
  570. package/src/foundation/index.js.map +0 -1
  571. package/src/foundation/local-database-contract.js.map +0 -1
  572. package/src/foundation/logging-preflight.js.map +0 -1
  573. package/src/foundation/logging.js.map +0 -1
  574. package/src/foundation/preflight.js.map +0 -1
  575. package/src/foundation/provision-database-bindings.js.map +0 -1
  576. package/src/foundation/provision-logical-database.js.map +0 -1
  577. package/src/foundation/requirements.js.map +0 -1
  578. package/src/foundation/router-origin-proof.js.map +0 -1
  579. package/src/foundation/stages.js.map +0 -1
  580. package/src/frontend-security/index.js.map +0 -1
  581. package/src/frontend-security/security-headers.js.map +0 -1
  582. package/src/generators/feature-registry.js.map +0 -1
  583. package/src/generators/function/function.js.map +0 -1
  584. package/src/generators/job/job.js.map +0 -1
  585. package/src/generators/logging-registry.js.map +0 -1
  586. package/src/generators/post-generation-install.js.map +0 -1
  587. package/src/generators/preset/generator.js.map +0 -1
  588. package/src/generators/preset/tool-versions.js.map +0 -1
  589. package/src/generators/preset/workspace-files-developer-agent.js.map +0 -1
  590. package/src/generators/preset/workspace-files-foundation-workload-roles.js.map +0 -1
  591. package/src/generators/preset/workspace-files-performance-cost.js.map +0 -1
  592. package/src/generators/preset/workspace-files-performance-load.js.map +0 -1
  593. package/src/generators/preset/workspace-files-workload-role-baseline.js.map +0 -1
  594. package/src/generators/preset/workspace-files.js.map +0 -1
  595. package/src/generators/repository-policy/repository-policy.js.map +0 -1
  596. package/src/generators/service/service.js.map +0 -1
  597. package/src/generators/slice/built-in-plan-api-contract-files.js.map +0 -1
  598. package/src/generators/slice/built-in-plan-api-files.js.map +0 -1
  599. package/src/generators/slice/built-in-plan-async-work-files.js.map +0 -1
  600. package/src/generators/slice/built-in-plan-authorization-files.js.map +0 -1
  601. package/src/generators/slice/built-in-plan-authorization-infrastructure-files.js.map +0 -1
  602. package/src/generators/slice/built-in-plan-data-deletion-files.js.map +0 -1
  603. package/src/generators/slice/built-in-plan-data-export-infrastructure-files.js.map +0 -1
  604. package/src/generators/slice/built-in-plan-data-files.js.map +0 -1
  605. package/src/generators/slice/built-in-plan-data-lifecycle-files.js.map +0 -1
  606. package/src/generators/slice/built-in-plan-data-lifecycle-runtime-files.js.map +0 -1
  607. package/src/generators/slice/built-in-plan-data-retention-job-files.js.map +0 -1
  608. package/src/generators/slice/built-in-plan-data-retention-launcher-files.js.map +0 -1
  609. package/src/generators/slice/built-in-plan-data-rights-files.js.map +0 -1
  610. package/src/generators/slice/built-in-plan-database-lifecycle-files.js.map +0 -1
  611. package/src/generators/slice/built-in-plan-database-retirement-rehearsal-files.js.map +0 -1
  612. package/src/generators/slice/built-in-plan-foundation-files.js.map +0 -1
  613. package/src/generators/slice/built-in-plan-frontend-files.js.map +0 -1
  614. package/src/generators/slice/built-in-plan-infrastructure-files.js.map +0 -1
  615. package/src/generators/slice/built-in-plan-infrastructure-safety-files.js.map +0 -1
  616. package/src/generators/slice/built-in-plan-metadata-files.js.map +0 -1
  617. package/src/generators/slice/built-in-plan-operations-files.js.map +0 -1
  618. package/src/generators/slice/built-in-plan-performance-files.js.map +0 -1
  619. package/src/generators/slice/built-in-plan-project-files.js.map +0 -1
  620. package/src/generators/slice/built-in-plan-release-files.js.map +0 -1
  621. package/src/generators/slice/built-in-plan-reliability-files.js.map +0 -1
  622. package/src/generators/slice/built-in-plan-secret-runtime-files.js.map +0 -1
  623. package/src/generators/slice/built-in-plan-shared.js.map +0 -1
  624. package/src/generators/slice/built-in-plan-testing-files.js.map +0 -1
  625. package/src/generators/slice/built-in-plan-verification-files.js.map +0 -1
  626. package/src/generators/slice/built-in-plan-web-files.js.map +0 -1
  627. package/src/generators/slice/built-in-plan-workload-role-files.js.map +0 -1
  628. package/src/generators/slice/built-in-plan.js.map +0 -1
  629. package/src/generators/slice/slice.js.map +0 -1
  630. package/src/generators/upgrade/upgrade.js.map +0 -1
  631. package/src/generators/workload/frozen-container.js.map +0 -1
  632. package/src/generators/workload/performance-registration.js.map +0 -1
  633. package/src/generators/workload/reliability-registration.js.map +0 -1
  634. package/src/generators/workload/service-performance-files.js.map +0 -1
  635. package/src/generators/workload/workload.js.map +0 -1
  636. package/src/git-policy/action-pins.js.map +0 -1
  637. package/src/git-policy/github-bootstrap.js.map +0 -1
  638. package/src/git-policy/index.js.map +0 -1
  639. package/src/git-policy/routing.js.map +0 -1
  640. package/src/index.js.map +0 -1
  641. package/src/infrastructure-change/convergence.js.map +0 -1
  642. package/src/infrastructure-change/drift.js.map +0 -1
  643. package/src/infrastructure-change/index.js.map +0 -1
  644. package/src/infrastructure-change/model.js.map +0 -1
  645. package/src/infrastructure-change/normalize.js.map +0 -1
  646. package/src/infrastructure-change/plan.js.map +0 -1
  647. package/src/infrastructure-change/risk.js.map +0 -1
  648. package/src/infrastructure-change/summary.js.map +0 -1
  649. package/src/local-dev/context-discovery.js.map +0 -1
  650. package/src/local-dev/contract-validation.js.map +0 -1
  651. package/src/local-dev/contracts.js.map +0 -1
  652. package/src/local-dev/control-contract.js.map +0 -1
  653. package/src/local-dev/dev-command-core.js.map +0 -1
  654. package/src/local-dev/effects.js.map +0 -1
  655. package/src/local-dev/environment-inspector.js.map +0 -1
  656. package/src/local-dev/environment-state.js.map +0 -1
  657. package/src/local-dev/errors.js.map +0 -1
  658. package/src/local-dev/fleet-client.js.map +0 -1
  659. package/src/local-dev/fleet-configuration.js.map +0 -1
  660. package/src/local-dev/fleet-inventory.js.map +0 -1
  661. package/src/local-dev/fleet-ipc.js.map +0 -1
  662. package/src/local-dev/fleet-launcher.js.map +0 -1
  663. package/src/local-dev/fleet-lock.js.map +0 -1
  664. package/src/local-dev/fleet-logs.js.map +0 -1
  665. package/src/local-dev/fleet-process.js.map +0 -1
  666. package/src/local-dev/fleet-runtime.js.map +0 -1
  667. package/src/local-dev/fleet-session.js.map +0 -1
  668. package/src/local-dev/fleet-slice-runner-daemon.js.map +0 -1
  669. package/src/local-dev/fleet-startup.js.map +0 -1
  670. package/src/local-dev/fleet-state.js.map +0 -1
  671. package/src/local-dev/fleet-supervisor-daemon.js.map +0 -1
  672. package/src/local-dev/fleet-supervisor.js.map +0 -1
  673. package/src/local-dev/gateway-daemon.js.map +0 -1
  674. package/src/local-dev/gateway.js.map +0 -1
  675. package/src/local-dev/identity.js.map +0 -1
  676. package/src/local-dev/index.js.map +0 -1
  677. package/src/local-dev/local-context.js.map +0 -1
  678. package/src/local-dev/nx-workspace.js.map +0 -1
  679. package/src/local-dev/personal-secret-ledger.js.map +0 -1
  680. package/src/local-dev/personal-stage.js.map +0 -1
  681. package/src/local-dev/portless.js.map +0 -1
  682. package/src/local-dev/ports.js.map +0 -1
  683. package/src/local-dev/private-state.js.map +0 -1
  684. package/src/local-dev/process-identity.js.map +0 -1
  685. package/src/local-dev/proxy.js.map +0 -1
  686. package/src/local-dev/runtime-paths.js.map +0 -1
  687. package/src/local-dev/secret-slice-claim.js.map +0 -1
  688. package/src/local-dev/slice-claim.js.map +0 -1
  689. package/src/local-dev/slice-runner.js.map +0 -1
  690. package/src/local-dev/tls.js.map +0 -1
  691. package/src/ownership/composition.js.map +0 -1
  692. package/src/ownership/foundation-record.js.map +0 -1
  693. package/src/ownership/hash.js.map +0 -1
  694. package/src/ownership/index.js.map +0 -1
  695. package/src/ownership/json-ownership.js.map +0 -1
  696. package/src/ownership/record.js.map +0 -1
  697. package/src/ownership/workspace-record.js.map +0 -1
  698. package/src/performance/cost-collection.js.map +0 -1
  699. package/src/performance/cost-controls.js.map +0 -1
  700. package/src/performance/evidence.js.map +0 -1
  701. package/src/performance/index.js.map +0 -1
  702. package/src/performance/load-safety.js.map +0 -1
  703. package/src/performance/model.js.map +0 -1
  704. package/src/performance/pr-artifact-budget.js.map +0 -1
  705. package/src/performance/runner.js.map +0 -1
  706. package/src/performance/scenario-endpoint.js.map +0 -1
  707. package/src/performance/scenario.js.map +0 -1
  708. package/src/performance/test-fixtures.d.ts +0 -7
  709. package/src/performance/test-fixtures.js +0 -169
  710. package/src/performance/test-fixtures.js.map +0 -1
  711. package/src/performance/unit-cost.js.map +0 -1
  712. package/src/release/index.js.map +0 -1
  713. package/src/release/lifecycle.js.map +0 -1
  714. package/src/release/manifest.js.map +0 -1
  715. package/src/release/rollout.js.map +0 -1
  716. package/src/release/source-authority.js.map +0 -1
  717. package/src/repository-policy/aurora-preflight.js.map +0 -1
  718. package/src/repository-policy/deployment-plan.js.map +0 -1
  719. package/src/repository-policy/foundation-profile.js.map +0 -1
  720. package/src/repository-policy/index.js.map +0 -1
  721. package/src/repository-policy/policy.js.map +0 -1
  722. package/src/secrets/adapters.js.map +0 -1
  723. package/src/secrets/aws-control-plane.js.map +0 -1
  724. package/src/secrets/declaration-verification.js.map +0 -1
  725. package/src/secrets/guardrails.js.map +0 -1
  726. package/src/secrets/index.js.map +0 -1
  727. package/src/secrets/metadata.js.map +0 -1
  728. package/src/secrets/model.js.map +0 -1
  729. package/src/secrets/operator.js.map +0 -1
  730. package/src/secrets/permissions.js.map +0 -1
  731. package/src/secrets/personal-command.js.map +0 -1
  732. package/src/secrets/personal-ledger.js.map +0 -1
  733. package/src/secrets/registry.js.map +0 -1
  734. package/src/secrets/verification.js.map +0 -1
  735. package/src/secrets/workload-access.js.map +0 -1
  736. package/src/supply-chain/action-pins.js.map +0 -1
  737. package/src/supply-chain/blueprint-trust-git.js.map +0 -1
  738. package/src/supply-chain/blueprint-trust-process.js.map +0 -1
  739. package/src/supply-chain/blueprint-trust.js.map +0 -1
  740. package/src/supply-chain/blueprint-verification-authority.js.map +0 -1
  741. package/src/supply-chain/blueprint-verification-execution.js.map +0 -1
  742. package/src/supply-chain/build-output.js.map +0 -1
  743. package/src/supply-chain/common.js.map +0 -1
  744. package/src/supply-chain/container-artifact-contract.js.map +0 -1
  745. package/src/supply-chain/container-release.js.map +0 -1
  746. package/src/supply-chain/ecr-applicability.js.map +0 -1
  747. package/src/supply-chain/ecr-scanning.js.map +0 -1
  748. package/src/supply-chain/ecs-deployed-image.js.map +0 -1
  749. package/src/supply-chain/errors.js.map +0 -1
  750. package/src/supply-chain/generated-blueprint-trust-command.js.map +0 -1
  751. package/src/supply-chain/generated-command.js.map +0 -1
  752. package/src/supply-chain/generated-container-release-command.js.map +0 -1
  753. package/src/supply-chain/generated-ecr-collector-command.js.map +0 -1
  754. package/src/supply-chain/generated-files.js.map +0 -1
  755. package/src/supply-chain/generated-scanner-command.js.map +0 -1
  756. package/src/supply-chain/index.js.map +0 -1
  757. package/src/supply-chain/inventory.js.map +0 -1
  758. package/src/supply-chain/license-expression.js.map +0 -1
  759. package/src/supply-chain/pnpm-toolchain-authority.js.map +0 -1
  760. package/src/supply-chain/policy.js.map +0 -1
  761. package/src/supply-chain/publication.js.map +0 -1
  762. package/src/supply-chain/sbom.js.map +0 -1
  763. package/src/supply-chain/scanner.js.map +0 -1
  764. package/src/supply-chain/vendored-source.js.map +0 -1
  765. package/src/testing-evidence/fixture-scope.js.map +0 -1
  766. package/src/testing-evidence/index.js.map +0 -1
  767. package/src/testing-evidence/quarantine.js.map +0 -1
  768. package/src/testing-evidence/result.js.map +0 -1
  769. package/src/testing-evidence/runner-adapter.js.map +0 -1
  770. package/src/testing-evidence/sensitive-artifacts.js.map +0 -1
  771. package/src/validation.js.map +0 -1
  772. package/src/work-markers/index.js.map +0 -1
  773. package/src/work-markers/work-markers.js.map +0 -1
  774. package/src/workload-roles/account-baseline.js.map +0 -1
  775. package/src/workload-roles/authority.js.map +0 -1
  776. package/src/workload-roles/aws-adapter.js.map +0 -1
  777. package/src/workload-roles/aws-internal-handler.js.map +0 -1
  778. package/src/workload-roles/aws-s3-state.js.map +0 -1
  779. package/src/workload-roles/aws-sdk-control-plane.js.map +0 -1
  780. package/src/workload-roles/boundary.js.map +0 -1
  781. package/src/workload-roles/convergence.js.map +0 -1
  782. package/src/workload-roles/effective-policy.js.map +0 -1
  783. package/src/workload-roles/handler.js.map +0 -1
  784. package/src/workload-roles/history.js.map +0 -1
  785. package/src/workload-roles/index.js.map +0 -1
  786. package/src/workload-roles/injection.js.map +0 -1
  787. package/src/workload-roles/manifest.js.map +0 -1
  788. package/src/workload-roles/provisioner.js.map +0 -1
  789. package/src/workload-roles/registration.js.map +0 -1
  790. package/src/workload-roles/stage-boundary.js.map +0 -1
  791. package/src/workload-roles/standard-slice.js.map +0 -1
@@ -7,8 +7,10 @@ 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");
12
14
  const index_js_3 = require("../../git-policy/index.js");
13
15
  const index_js_4 = require("../../repository-policy/index.js");
14
16
  const index_js_5 = require("../../release/index.js");
@@ -16,10 +18,14 @@ const index_js_6 = require("../../supply-chain/index.js");
16
18
  const deterministic_order_js_1 = require("../../deterministic-order.js");
17
19
  const tool_versions_js_1 = require("./tool-versions.js");
18
20
  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");
21
+ const workspace_files_ci_acceptance_js_1 = require("./workspace-files-ci-acceptance.js");
22
+ const workspace_files_ci_install_js_1 = require("./workspace-files-ci-install.js");
23
+ const workspace_files_adopter_skills_js_1 = require("./workspace-files-adopter-skills.js");
24
+ const workspace_files_ecr_inspector_js_1 = require("./workspace-files-ecr-inspector.js");
25
+ const workspace_files_thin_deploy_js_1 = require("./workspace-files-thin-deploy.js");
26
+ const workspace_files_thin_docs_js_1 = require("./workspace-files-thin-docs.js");
27
+ const workspace_files_thin_release_js_1 = require("./workspace-files-thin-release.js");
28
+ const workspace_files_stage_authority_js_1 = require("./workspace-files-stage-authority.js");
23
29
  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
30
  const GENERATED_RELEASE_ROLLOUT_PLAN_ID_PATTERN = JSON.stringify(index_js_5.RELEASE_ROLLOUT_PLAN_ID_PATTERN_SOURCE);
25
31
  exports.BLUEPRINT_TRUST_AUTHORITY_PATH = 'tools/blueprint-trust-check.mjs';
@@ -60,11 +66,12 @@ exports.PLAN_PROVIDER_REFRESH_GLOBAL_READ_ACTIONS = [
60
66
  'rds:DescribeDBProxies',
61
67
  'rds:DescribeDBSubnetGroups',
62
68
  ];
63
- function validatePlanProviderRefreshPolicy(value, region, account) {
69
+ function validatePlanProviderRefreshPolicy(value, region, account, stage) {
64
70
  if (!Array.isArray(value) ||
65
71
  value.length === 0 ||
66
72
  !/^[a-z]{2}(?:-gov)?-[a-z]+-\d$/.test(region) ||
67
- !/^\d{12}$/.test(account)) {
73
+ !/^\d{12}$/.test(account) ||
74
+ !['production', 'staging'].includes(stage)) {
68
75
  throw new Error('Provider refresh policy must be a non-empty exact statement array for one account and region.');
69
76
  }
70
77
  const allowed = new Set(exports.PLAN_PROVIDER_REFRESH_READ_ACTIONS);
@@ -117,6 +124,14 @@ function validatePlanProviderRefreshPolicy(value, region, account) {
117
124
  if (resources.some((resource) => {
118
125
  const arn = resource.split(':');
119
126
  const target = arn.slice(5).join(':');
127
+ const boundedPathWildcard = target.endsWith('/*') && !/[*?]/.test(target.slice(0, -2));
128
+ const boundedLambdaStagePrefix = arn[2] === 'lambda' &&
129
+ actions.every((action) => action.startsWith('lambda:') && !global.has(action)) &&
130
+ new RegExp('^function:(?:' +
131
+ stage +
132
+ '-|[A-Za-z0-9][A-Za-z0-9_-]*-' +
133
+ stage +
134
+ '-)[A-Za-z0-9_-]*\\*$').test(target);
120
135
  return (arn[0] !== 'arn' ||
121
136
  arn[1] !== 'aws' ||
122
137
  !/^[a-z0-9-]+$/.test(arn[2] ?? '') ||
@@ -124,7 +139,8 @@ function validatePlanProviderRefreshPolicy(value, region, account) {
124
139
  !['', account].includes(arn[4] ?? '') ||
125
140
  !target ||
126
141
  (/[*?]/.test(target) &&
127
- (!target.endsWith('/*') || /[*?]/.test(target.slice(0, -2)))));
142
+ !boundedPathWildcard &&
143
+ !boundedLambdaStagePrefix));
128
144
  })) {
129
145
  throw new Error(`Provider refresh statement ${index} is not bounded to exact targets in the selected account and region.`);
130
146
  }
@@ -161,6 +177,51 @@ const TOOL_VERSIONS = {
161
177
  function json(value) {
162
178
  return `${JSON.stringify(value, null, 2)}\n`;
163
179
  }
180
+ function indentWorkflowScript(source, spaces) {
181
+ const prefix = ' '.repeat(spaces);
182
+ return source
183
+ .trim()
184
+ .split('\n')
185
+ .map((line) => `${prefix}${line}`)
186
+ .join('\n');
187
+ }
188
+ function protectedSecretPolicyDerivationScript() {
189
+ return `node --input-type=module <<'NODE'
190
+ import { appendFileSync } from 'node:fs';
191
+ import {
192
+ configuredDeploymentAccountForStage,
193
+ configuredDeploymentRegion,
194
+ isActiveDeploymentStage,
195
+ readRepositoryOperatingPolicy,
196
+ } from '@empire-builder-kit/nx/repository-policy';
197
+
198
+ const output = process.env.GITHUB_OUTPUT?.trim() ?? '';
199
+ const renderedProfile = process.env.EBK_RENDERED_PROFILE?.trim() ?? '';
200
+ const stage = process.env.EBK_STAGE?.trim() ?? '';
201
+ if (!output) throw new Error('Secret workflow authority output is unavailable.');
202
+ const policy = readRepositoryOperatingPolicy(process.cwd());
203
+ if (policy.deploymentProfile !== renderedProfile) {
204
+ throw new Error('Generated and committed deployment profiles differ.');
205
+ }
206
+ if (!isActiveDeploymentStage(policy.deploymentProfile, stage)) {
207
+ throw new Error('Secret workflow stage is inactive for the committed deployment profile.');
208
+ }
209
+ configuredDeploymentAccountForStage(
210
+ policy.deploymentProfile,
211
+ policy.deploymentAccounts,
212
+ stage,
213
+ );
214
+ const accounts = policy.deploymentAccounts;
215
+ const region = configuredDeploymentRegion(policy.deploymentRegion);
216
+ appendFileSync(output, [
217
+ 'deployment_region=' + region,
218
+ 'development_account_id=' + (accounts.development ?? ''),
219
+ 'staging_account_id=' + (accounts.staging ?? ''),
220
+ 'production_account_id=' + (accounts.production ?? ''),
221
+ '',
222
+ ].join('\\n'));
223
+ NODE`;
224
+ }
164
225
  function planApplicationBindingCheck() {
165
226
  return ` node - <<'NODE'
166
227
  const declared = JSON.parse(process.env.AWS_PLAN_SST_APPS_JSON ?? '');
@@ -474,6 +535,8 @@ function planRoleAuditStep(branch, mode = 'produce') {
474
535
  EBK_PLAN_ENVIRONMENT: infrastructure-plan-${stage}
475
536
  EBK_PLAN_ROLE_ARN: \${{ env.AWS_PLAN_ROLE_ARN }}
476
537
  EBK_PLAN_REPOSITORY: \${{ github.repository }}
538
+ EBK_PLAN_REPOSITORY_ID: \${{ github.repository_id }}
539
+ EBK_PLAN_REPOSITORY_OWNER_ID: \${{ github.repository_owner_id }}
477
540
  run: |
478
541
  role_name="\${EBK_PLAN_ROLE_ARN##*/}"
479
542
  test "$(aws sts get-caller-identity --query Account --output text)" = "$AWS_EXPECTED_ACCOUNT_ID" ||
@@ -506,8 +569,23 @@ function planRoleAuditStep(branch, mode = 'produce') {
506
569
  const role = JSON.parse(readFileSync(process.argv[2], 'utf8')).Role;
507
570
  const policy = JSON.parse(readFileSync(process.argv[3], 'utf8')).PolicyDocument;
508
571
  const account = process.env.AWS_EXPECTED_ACCOUNT_ID;
572
+ const repository = process.env.EBK_PLAN_REPOSITORY ?? '';
573
+ const repositoryId = process.env.EBK_PLAN_REPOSITORY_ID ?? '';
574
+ const repositoryOwnerId = process.env.EBK_PLAN_REPOSITORY_OWNER_ID ?? '';
509
575
  const stateBucketArn = process.env.AWS_PLAN_STATE_BUCKET_ARN;
510
576
  const stateKmsKeyArn = process.env.AWS_PLAN_STATE_KMS_KEY_ARN;
577
+ if (
578
+ !/^[A-Za-z0-9](?:[A-Za-z0-9-]{0,38})\\/[A-Za-z0-9_.-]{1,100}$/.test(
579
+ repository,
580
+ ) ||
581
+ !/^[1-9][0-9]*$/.test(repositoryOwnerId) ||
582
+ !/^[1-9][0-9]*$/.test(repositoryId)
583
+ ) throw new Error('Exact immutable GitHub repository identity is required.');
584
+ const [repositoryOwner, repositoryName] = repository.split('/');
585
+ const oidcSubject =
586
+ 'repo:' + repositoryOwner + '@' + repositoryOwnerId + '/' +
587
+ repositoryName + '@' + repositoryId + ':environment:' +
588
+ process.env.EBK_PLAN_ENVIRONMENT;
511
589
  const parseExactArnArray = (name, pattern) => {
512
590
  let value;
513
591
  try {
@@ -551,6 +629,7 @@ function planRoleAuditStep(branch, mode = 'produce') {
551
629
  'AWS_PLAN_GENERATED_SECRET_ARNS_JSON',
552
630
  new RegExp('^arn:aws:secretsmanager:' + process.env.AWS_REGION + ':' + account + ':secret:ebk/'),
553
631
  );
632
+ const stateStage = process.env.EBK_PLAN_BRANCH === 'main' ? 'production' : 'staging';
554
633
  let providerRefreshPolicy;
555
634
  try {
556
635
  providerRefreshPolicy = JSON.parse(
@@ -614,6 +693,23 @@ function planRoleAuditStep(branch, mode = 'produce') {
614
693
  for (const resource of resources) {
615
694
  const arn = resource.split(':');
616
695
  const target = arn.slice(5).join(':');
696
+ const boundedPathWildcard =
697
+ target.endsWith('/*') &&
698
+ !/[*?]/.test(target.slice(0, -2));
699
+ const boundedLambdaStagePrefix =
700
+ arn[2] === 'lambda' &&
701
+ actions.every(
702
+ (action) =>
703
+ action.startsWith('lambda:') &&
704
+ !globalProviderReadActions.has(action),
705
+ ) &&
706
+ new RegExp(
707
+ '^function:(?:' +
708
+ stateStage +
709
+ '-|[A-Za-z0-9][A-Za-z0-9_-]*-' +
710
+ stateStage +
711
+ '-)[A-Za-z0-9_-]*\\\\*$',
712
+ ).test(target);
617
713
  if (
618
714
  arn[0] !== 'arn' ||
619
715
  arn[1] !== 'aws' ||
@@ -622,7 +718,8 @@ function planRoleAuditStep(branch, mode = 'produce') {
622
718
  !['', account].includes(arn[4] ?? '') ||
623
719
  !target ||
624
720
  (/[*?]/.test(target) &&
625
- (!target.endsWith('/*') || /[*?]/.test(target.slice(0, -2))))
721
+ !boundedPathWildcard &&
722
+ !boundedLambdaStagePrefix)
626
723
  ) throw new Error('Provider refresh statement ' + index + ' has a target outside the selected account and region.');
627
724
  }
628
725
  }
@@ -637,7 +734,6 @@ function planRoleAuditStep(branch, mode = 'produce') {
637
734
  ).sort((left, right) =>
638
735
  left.Sid < right.Sid ? -1 : left.Sid > right.Sid ? 1 : 0,
639
736
  );
640
- const stateStage = process.env.EBK_PLAN_BRANCH === 'main' ? 'production' : 'staging';
641
737
  if (
642
738
  stateKmsKeyArn &&
643
739
  stateKmsKeyArn !==
@@ -681,7 +777,7 @@ function planRoleAuditStep(branch, mode = 'produce') {
681
777
  Action: 'sts:AssumeRoleWithWebIdentity',
682
778
  Condition: { StringEquals: {
683
779
  '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,
780
+ 'token.actions.githubusercontent.com:sub': oidcSubject,
685
781
  }},
686
782
  }],
687
783
  };
@@ -703,11 +799,24 @@ function planRoleAuditStep(branch, mode = 'produce') {
703
799
  Sid: 'ObserveInfrastructureIdentity',
704
800
  Effect: 'Allow',
705
801
  Action: [
706
- 'resourcegroupstaggingapi:GetResources',
802
+ 'tag:GetResources',
707
803
  'sts:GetCallerIdentity',
708
804
  ],
709
805
  Resource: '*',
710
806
  },
807
+ {
808
+ Sid: 'ObserveStageSecretVersionIdentity',
809
+ Effect: 'Allow',
810
+ Action: 'secretsmanager:ListSecretVersionIds',
811
+ Resource:
812
+ 'arn:aws:secretsmanager:' + process.env.AWS_REGION + ':' +
813
+ account + ':secret:*',
814
+ Condition: {
815
+ StringEquals: {
816
+ 'aws:ResourceTag/ebk:stage': stateStage,
817
+ },
818
+ },
819
+ },
711
820
  {
712
821
  Sid: 'ValidatePlannedIamPolicies',
713
822
  Effect: 'Allow',
@@ -782,7 +891,10 @@ function planRoleAuditStep(branch, mode = 'produce') {
782
891
  generatedSecretArns,
783
892
  providerRefreshStatements,
784
893
  region: process.env.AWS_REGION,
785
- repository: process.env.EBK_PLAN_REPOSITORY,
894
+ repository,
895
+ repositoryId,
896
+ repositoryOwnerId,
897
+ oidcSubject,
786
898
  role: {
787
899
  arn: role.Arn,
788
900
  policy: canonical(policy),
@@ -826,11 +938,17 @@ function planRoleAuditJob(stage, configureAwsAction) {
826
938
  const production = stage === 'production';
827
939
  const branch = production ? 'main' : 'develop';
828
940
  const prefix = production ? 'AWS_PRODUCTION_PLAN' : 'AWS_STAGING_PLAN';
941
+ const profileAdmission = production
942
+ ? ''
943
+ : ` (github.event_name == 'workflow_dispatch' ||\n needs.deployment-mode.outputs.profile != 'starter') &&\n`;
829
944
  return ` infrastructure-plan-role-audit-${stage}:
830
945
  name: audit ${stage} infrastructure plan role
831
- needs: route
946
+ needs: [route, deployment-mode]
832
947
  if: |
833
- github.ref == 'refs/heads/${branch}' &&
948
+ always() &&
949
+ needs.route.result == 'success' &&
950
+ needs.deployment-mode.result == 'success' &&
951
+ ${profileAdmission} github.ref == 'refs/heads/${branch}' &&
834
952
  (github.event_name == 'push' && 'deploy' || inputs.action) != 'unlock'
835
953
  runs-on: ubuntu-latest
836
954
  environment: infrastructure-plan-${stage}
@@ -938,6 +1056,7 @@ const SUPPLY_CHAIN_WORKSPACE_FILE_PLAN = (0, index_js_6.renderSupplyChainWorkspa
938
1056
  generatedAt: '1970-01-01T00:00:00.000Z',
939
1057
  });
940
1058
  const WORKSPACE_SEEDED_PATHS = new Set([
1059
+ dependency_overrides_js_1.DEPENDENCY_OVERRIDES_PATH,
941
1060
  ...SUPPLY_CHAIN_WORKSPACE_FILE_PLAN.seeded,
942
1061
  ...workspace_files_developer_agent_js_1.DEVELOPER_AGENT_WORKSPACE_SEEDED_PATHS,
943
1062
  '.github/CODEOWNERS',
@@ -947,6 +1066,8 @@ const WORKSPACE_SEEDED_PATHS = new Set([
947
1066
  'docs/architecture.md',
948
1067
  'docs/git-workflow.md',
949
1068
  'docs/repository-operating-policy.md',
1069
+ 'docs/stage-deployment-authority.md',
1070
+ 'docs/workspace-deployment-ownership.md',
950
1071
  'docs/work-markers.md',
951
1072
  'ebk.repository-policy.json',
952
1073
  ]);
@@ -964,21 +1085,23 @@ exports.WORKSPACE_PROJECT_MANAGED_JSON_POINTERS = [
964
1085
  '/root',
965
1086
  '/tags',
966
1087
  '/targets/blueprint-trust-check',
1088
+ '/targets/blueprint-trust-check-main',
967
1089
  '/targets/blueprint-trust-check-protected',
968
1090
  '/targets/developer-context',
969
1091
  '/targets/deployment-plan',
1092
+ '/targets/deploy-local',
970
1093
  '/targets/dev',
971
1094
  '/targets/dev-doctor',
972
1095
  '/targets/dev-setup',
973
1096
  '/targets/github-bootstrap',
974
1097
  '/targets/install-git-hooks',
1098
+ '/targets/stage-authority-render',
975
1099
  '/targets/supply-chain-check',
976
1100
  '/targets/supply-chain-check-protected',
977
1101
  '/targets/supply-chain-scan',
978
1102
  '/targets/verify',
1103
+ '/targets/verify-proposed',
979
1104
  '/targets/work-markers',
980
- '/targets/workload-role-candidate-report',
981
- '/targets/workload-role-provisioner-build',
982
1105
  '/targets/workspace-health',
983
1106
  ];
984
1107
  const FOUNDATION_SEEDED_PATHS = new Set([
@@ -986,6 +1109,10 @@ const FOUNDATION_SEEDED_PATHS = new Set([
986
1109
  'docs/costs.md',
987
1110
  'docs/runbooks.md',
988
1111
  'docs/security.md',
1112
+ 'performance/account-cost-policy.json',
1113
+ 'security/router-waf-blocking-rehearsal.json',
1114
+ 'security/router-waf-live-evidence.json',
1115
+ 'security/router-waf.json',
989
1116
  ]);
990
1117
  function createFoundationOwnership(files, frameworkVersion) {
991
1118
  const paths = Object.keys(files)
@@ -1048,7 +1175,48 @@ function createWorkspaceOwnership(files, frameworkVersion) {
1048
1175
  }
1049
1176
  function renderWorkspaceFiles(options) {
1050
1177
  const { frameworkVersion, name } = options;
1178
+ const foundationPlan = (0, capability_plan_js_1.planFoundationCapabilities)({
1179
+ current: options.foundationDescriptor,
1180
+ requirements: options.foundationRequirements ?? [],
1181
+ });
1182
+ const dependencyOverrides = (0, dependency_overrides_js_1.parseDependencyOverrides)(options.dependencyOverrides === undefined
1183
+ ? { schemaVersion: 1, overrides: {} }
1184
+ : options.dependencyOverrides);
1185
+ const deploymentProfile = options.deploymentProfile ?? 'starter';
1186
+ const deliveryLane = options.deliveryLane ?? 'protected-pair';
1187
+ if (deliveryLane === 'trunk' && deploymentProfile !== 'starter') {
1188
+ throw new Error('The trunk delivery lane requires the starter deployment profile.');
1189
+ }
1190
+ const trunkLane = deliveryLane === 'trunk';
1191
+ const productionApprovalMode = options.productionApprovalMode ?? 'promotion-pr';
1192
+ if (productionApprovalMode === 'trunk-promotion' &&
1193
+ (deploymentProfile === 'starter' || trunkLane)) {
1194
+ throw new Error('trunk-promotion requires an isolated deployment profile on the protected pair.');
1195
+ }
1196
+ const trunkPromotion = productionApprovalMode === 'trunk-promotion';
1197
+ const protectedBranches = trunkLane ? '[main]' : '[develop, main]';
1198
+ const activeStages = (0, index_js_4.activeDeploymentStages)(deploymentProfile);
1199
+ const protectedWorkflowStageChoices = deploymentProfile === 'starter'
1200
+ ? 'development, production'
1201
+ : deploymentProfile === 'production-isolated'
1202
+ ? 'production'
1203
+ : 'staging, production';
1204
+ const ecrInspectorWorkflowStageChoices = deploymentProfile === 'starter'
1205
+ ? 'production'
1206
+ : protectedWorkflowStageChoices;
1207
+ const rehearsalStage = (0, index_js_4.productionRehearsalStage)(deploymentProfile);
1208
+ const protectedWorkflowStageBranchCases = `${rehearsalStage}:refs/heads/develop|production:refs/heads/main`;
1209
+ const workspaceDeploymentId = options.workspaceDeploymentId ??
1210
+ `ebkws-${(0, index_js_1.hashGeneratedContent)(`workspace-deployment-id:${name}`).slice(0, 32)}`;
1211
+ const initialRepositoryPolicy = (0, index_js_4.createIncubationRepositoryPolicy)(deploymentProfile, workspaceDeploymentId);
1212
+ initialRepositoryPolicy.deploymentAccounts = options.deploymentAccounts ?? {
1213
+ development: null,
1214
+ production: null,
1215
+ staging: null,
1216
+ };
1217
+ initialRepositoryPolicy.deploymentRegion = options.deploymentRegion ?? null;
1051
1218
  const developerAgentFilePlan = (0, workspace_files_developer_agent_js_1.renderDeveloperAgentWorkspaceFilePlan)({
1219
+ deploymentProfile,
1052
1220
  frameworkVersion,
1053
1221
  name,
1054
1222
  });
@@ -1059,7 +1227,38 @@ function renderWorkspaceFiles(options) {
1059
1227
  const pnpmAction = (0, index_js_3.pinnedAction)(index_js_3.GITHUB_ACTION_PINS.pnpmSetup);
1060
1228
  const setupNodeAction = (0, index_js_3.pinnedAction)(index_js_3.GITHUB_ACTION_PINS.setupNode);
1061
1229
  const uploadArtifactAction = (0, index_js_3.pinnedAction)(index_js_3.GITHUB_ACTION_PINS.uploadArtifact);
1230
+ const cacheAction = (0, index_js_3.pinnedAction)(index_js_3.GITHUB_ACTION_PINS.cache);
1231
+ const nxSetShasAction = (0, index_js_3.pinnedAction)(index_js_3.GITHUB_ACTION_PINS.nxSetShas);
1232
+ // GitHub scopes cache reads to the current branch and its base, so protected
1233
+ // pushes never restore entries written by pull-request runs.
1234
+ const nxCacheSteps = ` - name: Restore the Nx task cache
1235
+ uses: ${cacheAction}
1236
+ with:
1237
+ path: .nx/cache
1238
+ key: nx-\${{ runner.os }}-\${{ hashFiles('pnpm-lock.yaml') }}-\${{ github.sha }}
1239
+ restore-keys: |
1240
+ nx-\${{ runner.os }}-\${{ hashFiles('pnpm-lock.yaml') }}-`;
1241
+ // Isolated profiles bind the staging release candidate when promoting to
1242
+ // production, so a main push never needs a rebuilt candidate.
1243
+ const releaseCandidateBranchCondition = deploymentProfile === 'starter'
1244
+ ? ''
1245
+ : " &&\n github.ref != 'refs/heads/main'";
1246
+ // In the trunk lane no pull request is a protected route: main is the only
1247
+ // protected branch and every feature pull request targets it.
1248
+ const protectedDevelopPromotion = trunkLane
1249
+ ? 'false'
1250
+ : "github.event.pull_request.head.repo.full_name == github.repository && github.event.pull_request.head.ref == 'develop' && github.event.pull_request.base.ref == 'main'";
1251
+ const protectedMainSync = trunkLane
1252
+ ? 'false'
1253
+ : "github.event.pull_request.head.repo.full_name == github.repository && github.event.pull_request.head.ref == 'main' && github.event.pull_request.base.ref == 'develop'";
1254
+ const protectedPush = trunkLane
1255
+ ? "github.event_name == 'push' && github.ref == 'refs/heads/main'"
1256
+ : "github.event_name == 'push' && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/develop')";
1257
+ const protectedDeploymentRouteJobName = 'protected deployment route';
1258
+ const stagingApplyJobPrefix = 'apply staging plan ';
1062
1259
  const files = {
1260
+ ...(0, workspace_files_ci_acceptance_js_1.renderWorkspaceCiAcceptanceFiles)(),
1261
+ ...(0, workspace_files_ci_install_js_1.renderWorkspaceCiInstallFiles)(),
1063
1262
  '.node-version': `${tool_versions_js_1.WORKSPACE_NODE_VERSION}\n`,
1064
1263
  '.github/CODEOWNERS': `# Replace this placeholder with the GitHub user or team that owns protected source.
1065
1264
  # GitHub bootstrap remains blocked until a non-placeholder catch-all owner is committed.
@@ -1069,21 +1268,83 @@ function renderWorkspaceFiles(options) {
1069
1268
 
1070
1269
  on:
1071
1270
  pull_request:
1072
- branches: [develop, main]
1271
+ branches: ${protectedBranches}
1073
1272
  push:
1074
- branches: [develop, main]
1273
+ branches: ${protectedBranches}
1075
1274
  workflow_dispatch:
1076
1275
 
1077
1276
  concurrency:
1078
- group: verify-\${{ github.ref }}
1079
- cancel-in-progress: true
1277
+ group: verify-\${{ github.event_name == 'push' && github.sha || github.ref }}
1278
+ cancel-in-progress: \${{ github.event_name != 'push' }}
1080
1279
 
1081
1280
  permissions:
1281
+ actions: read
1082
1282
  contents: read
1283
+ pull-requests: read
1284
+
1285
+ env:
1286
+ EBK_CI_CANDIDATE_SHA: \${{ github.event.pull_request.head.sha || github.sha }}
1083
1287
 
1084
1288
  jobs:
1289
+ acceptance:
1290
+ name: accepted merge evidence
1291
+ if: ${protectedPush}
1292
+ runs-on: ubuntu-latest
1293
+ outputs:
1294
+ history_only: \${{ steps.resolve.outputs.history_only == 'true' && steps.restore.outcome == 'success' && steps.verify.outcome == 'success' }}
1295
+ reused: \${{ steps.resolve.outputs.reused == 'true' && steps.restore.outcome == 'success' && steps.verify.outcome == 'success' }}
1296
+ route: \${{ steps.resolve.outputs.reused == 'true' && steps.restore.outcome == 'success' && steps.verify.outcome == 'success' && steps.resolve.outputs.route || '' }}
1297
+ steps:
1298
+ - name: Check out immutable merged candidate
1299
+ if: ${protectedPush}
1300
+ uses: ${checkoutAction}
1301
+ with:
1302
+ fetch-depth: 0
1303
+ ref: \${{ github.sha }}
1304
+ - name: Resolve exact protected-route acceptance
1305
+ if: ${protectedPush}
1306
+ id: resolve
1307
+ env:
1308
+ GH_TOKEN: \${{ github.token }}
1309
+ run: node tools/ci/workspace-acceptance.mjs resolve
1310
+ - name: Restore exact protected-route acceptance
1311
+ if: steps.resolve.outputs.reused == 'true'
1312
+ id: restore
1313
+ continue-on-error: true
1314
+ env:
1315
+ EBK_ACCEPTANCE_ARTIFACT_DIGEST: \${{ steps.resolve.outputs.artifact_digest }}
1316
+ EBK_ACCEPTANCE_ARTIFACT_ID: \${{ steps.resolve.outputs.artifact_id }}
1317
+ EBK_ACCEPTANCE_ARTIFACT_SIZE: \${{ steps.resolve.outputs.artifact_size }}
1318
+ GH_TOKEN: \${{ github.token }}
1319
+ run: >-
1320
+ node tools/ci/workspace-acceptance.mjs download
1321
+ .ebk/evidence/reused-acceptance
1322
+ "$EBK_ACCEPTANCE_ARTIFACT_ID"
1323
+ "$EBK_ACCEPTANCE_ARTIFACT_DIGEST"
1324
+ "$EBK_ACCEPTANCE_ARTIFACT_SIZE"
1325
+ - name: Verify exact merged-tree acceptance
1326
+ if: steps.resolve.outputs.reused == 'true' && steps.restore.outcome == 'success'
1327
+ id: verify
1328
+ continue-on-error: true
1329
+ env:
1330
+ EBK_ACCEPTANCE_BASE: \${{ steps.resolve.outputs.base_commit }}
1331
+ EBK_ACCEPTANCE_HEAD: \${{ steps.resolve.outputs.head_commit }}
1332
+ EBK_ACCEPTANCE_ROUTE: \${{ steps.resolve.outputs.route }}
1333
+ EBK_ACCEPTANCE_RUN_ATTEMPT: \${{ steps.resolve.outputs.run_attempt }}
1334
+ EBK_ACCEPTANCE_RUN_ID: \${{ steps.resolve.outputs.run_id }}
1335
+ run: >-
1336
+ node tools/ci/workspace-acceptance.mjs verify
1337
+ .ebk/evidence/reused-acceptance/manifest.json
1338
+ "$EBK_ACCEPTANCE_HEAD"
1339
+ "$EBK_ACCEPTANCE_BASE"
1340
+ "$EBK_ACCEPTANCE_ROUTE"
1341
+ "$EBK_ACCEPTANCE_RUN_ID"
1342
+ "$EBK_ACCEPTANCE_RUN_ATTEMPT"
1343
+
1085
1344
  policy:
1086
1345
  name: policy
1346
+ needs: acceptance
1347
+ if: always() && needs.acceptance.outputs.reused != 'true'
1087
1348
  runs-on: ubuntu-latest
1088
1349
  steps:
1089
1350
  - uses: ${checkoutAction}
@@ -1095,31 +1356,118 @@ jobs:
1095
1356
  with:
1096
1357
  node-version: ${tool_versions_js_1.WORKSPACE_NODE_VERSION}
1097
1358
  cache: pnpm
1098
- - run: pnpm install --frozen-lockfile
1359
+ - run: node tools/ci/install.mjs
1099
1360
  - run: node tools/ebk-git-policy.mjs
1361
+ env:
1362
+ EBK_HEAD_REPOSITORY: \${{ github.event.pull_request.head.repo.full_name }}
1100
1363
 
1101
1364
  verify:
1102
1365
  name: verify
1366
+ needs: acceptance
1367
+ if: always() && needs.acceptance.outputs.reused != 'true'
1103
1368
  runs-on: ubuntu-latest
1104
1369
  steps:
1105
1370
  - uses: ${checkoutAction}
1371
+ with:
1372
+ fetch-depth: 0
1373
+ ref: \${{ github.event.pull_request.head.sha || github.sha }}
1106
1374
  - uses: ${pnpmAction}
1107
1375
  - uses: ${setupNodeAction}
1108
1376
  with:
1109
1377
  node-version: ${tool_versions_js_1.WORKSPACE_NODE_VERSION}
1110
1378
  cache: pnpm
1111
- - run: pnpm install --frozen-lockfile
1112
- - run: pnpm nx format:check
1113
- - run: pnpm nx run-many -t verify --parallel=3
1379
+ - run: node tools/ci/install.mjs
1380
+ ${nxCacheSteps}
1381
+ - run: pnpm format:check
1382
+ - name: Resolve the affected base and head for proposed work
1383
+ if: github.event_name == 'pull_request' && !(${protectedDevelopPromotion} || ${protectedMainSync})
1384
+ uses: ${nxSetShasAction}
1385
+ - name: Verify proposed workspace
1386
+ if: github.event_name == 'pull_request' && !(${protectedDevelopPromotion} || ${protectedMainSync})
1387
+ run: |
1388
+ pnpm nx run ${name}:verify-proposed
1389
+ pnpm nx affected -t verify --exclude=${name} --parallel=3
1390
+ - name: Verify protected develop promotion
1391
+ if: github.event_name == 'pull_request' && ${protectedDevelopPromotion}
1392
+ run: pnpm nx run-many -t verify --parallel=3
1393
+ - name: Verify protected main sync
1394
+ if: github.event_name == 'pull_request' && ${protectedMainSync}
1395
+ run: |
1396
+ pnpm nx run ${name}:blueprint-trust-check-main
1397
+ pnpm nx run ${name}:verify-proposed
1398
+ pnpm nx run-many -t verify --exclude=${name} --parallel=3
1399
+ - name: Verify adopted workspace
1400
+ if: github.event_name != 'pull_request'
1401
+ run: pnpm nx run-many -t verify --parallel=3
1402
+
1403
+ seal-acceptance:
1404
+ name: seal protected route acceptance
1405
+ needs: [policy, verify]
1406
+ if: >-
1407
+ always() &&
1408
+ needs.policy.result == 'success' &&
1409
+ needs.verify.result == 'success' &&
1410
+ github.event_name == 'pull_request' &&
1411
+ (${protectedDevelopPromotion} || ${protectedMainSync})
1412
+ runs-on: ubuntu-latest
1413
+ steps:
1414
+ - name: Check out immutable accepted candidate
1415
+ uses: ${checkoutAction}
1416
+ with:
1417
+ fetch-depth: 0
1418
+ ref: \${{ env.EBK_CI_CANDIDATE_SHA }}
1419
+ - name: Seal exact protected-route acceptance
1420
+ id: seal
1421
+ env:
1422
+ EBK_BASE_COMMIT: \${{ github.event.pull_request.base.sha }}
1423
+ EBK_HEAD_REPOSITORY: \${{ github.event.pull_request.head.repo.full_name }}
1424
+ run: >-
1425
+ node tools/ci/workspace-acceptance.mjs seal
1426
+ .ebk/evidence/ci/workspace-acceptance/manifest.json
1427
+ - name: Retain exact protected-route acceptance
1428
+ if: steps.seal.outputs.sealed == 'true'
1429
+ uses: ${uploadArtifactAction}
1430
+ with:
1431
+ name: workspace-acceptance-\${{ env.EBK_CI_CANDIDATE_SHA }}-\${{ github.run_id }}-\${{ github.run_attempt }}
1432
+ path: .ebk/evidence/ci/workspace-acceptance
1433
+ if-no-files-found: error
1434
+ include-hidden-files: true
1435
+ retention-days: 30
1436
+
1437
+ release-candidate:
1438
+ name: build immutable release candidates
1439
+ needs: [acceptance, policy, verify]
1440
+ if: >-
1441
+ always() &&
1442
+ github.event_name == 'push' &&
1443
+ needs.acceptance.outputs.history_only != 'true' &&
1444
+ (needs.acceptance.outputs.reused == 'true' ||
1445
+ (needs.policy.result == 'success' && needs.verify.result == 'success'))${releaseCandidateBranchCondition}
1446
+ runs-on: ubuntu-latest
1447
+ steps:
1448
+ - uses: ${checkoutAction}
1449
+ with:
1450
+ fetch-depth: 0
1451
+ ref: \${{ github.sha }}
1452
+ - uses: ${pnpmAction}
1453
+ - uses: ${setupNodeAction}
1454
+ with:
1455
+ node-version: ${tool_versions_js_1.WORKSPACE_NODE_VERSION}
1456
+ cache: pnpm
1457
+ - run: node tools/ci/install.mjs
1458
+ ${nxCacheSteps}
1114
1459
  - name: Build immutable release candidates
1115
- if: github.event_name == 'push'
1460
+ id: candidates
1116
1461
  env:
1117
1462
  EBK_SOURCE_COMMIT: \${{ github.sha }}
1118
1463
  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
1464
+ slice_projects="$(node tools/ci/release-candidates.mjs)"
1465
+ if [ -z "$slice_projects" ]; then
1466
+ echo 'No application release producers; application artifacts are not applicable.'
1467
+ echo 'available=false' >> "$GITHUB_OUTPUT"
1468
+ exit 0
1469
+ fi
1470
+ pnpm nx run-many -t release-candidate --projects="$slice_projects" --parallel=3
1123
1471
  mkdir -p .ebk/evidence/release-candidate/packages
1124
1472
  IFS=',' read -r -a projects <<< "$slice_projects"
1125
1473
  for project in "\${projects[@]}"; do
@@ -1129,8 +1477,9 @@ jobs:
1129
1477
  cp packages/"$project"/.ebk/evidence/release-candidate/*.json \
1130
1478
  ".ebk/evidence/release-candidate/packages/$project/"
1131
1479
  done
1480
+ echo 'available=true' >> "$GITHUB_OUTPUT"
1132
1481
  - name: Retain exact immutable release candidate
1133
- if: github.event_name == 'push'
1482
+ if: steps.candidates.outputs.available == 'true'
1134
1483
  uses: ${uploadArtifactAction}
1135
1484
  with:
1136
1485
  name: release-candidate-\${{ github.sha }}
@@ -1138,6 +1487,7 @@ jobs:
1138
1487
  include-hidden-files: true
1139
1488
  retention-days: 30
1140
1489
  path: .ebk/evidence/release-candidate
1490
+ ${(0, workspace_files_thin_deploy_js_1.renderThinDeployVerifyCaller)(deploymentProfile, deliveryLane, productionApprovalMode)}
1141
1491
  `,
1142
1492
  '.github/workflows/deploy.yml': `name: Deploy
1143
1493
 
@@ -1165,7 +1515,97 @@ permissions:
1165
1515
  contents: read
1166
1516
 
1167
1517
  jobs:
1518
+ history-classification:
1519
+ name: classify protected history-only sync
1520
+ if: github.event_name == 'push' && github.ref == 'refs/heads/develop'
1521
+ runs-on: ubuntu-latest
1522
+ permissions:
1523
+ actions: read
1524
+ contents: read
1525
+ pull-requests: read
1526
+ outputs:
1527
+ history_only: \${{ steps.resolve.outputs.history_only == 'true' && steps.restore.outcome == 'success' && steps.verify.outcome == 'success' }}
1528
+ steps:
1529
+ - name: Check out immutable protected candidate
1530
+ uses: ${checkoutAction}
1531
+ with:
1532
+ fetch-depth: 0
1533
+ ref: \${{ github.sha }}
1534
+ - uses: ${setupNodeAction}
1535
+ with:
1536
+ node-version: ${tool_versions_js_1.WORKSPACE_NODE_VERSION}
1537
+ - name: Resolve exact protected-route acceptance
1538
+ id: resolve
1539
+ env:
1540
+ GH_TOKEN: \${{ github.token }}
1541
+ run: node tools/ci/workspace-acceptance.mjs resolve
1542
+ - name: Restore exact protected-route acceptance
1543
+ if: steps.resolve.outputs.reused == 'true'
1544
+ id: restore
1545
+ continue-on-error: true
1546
+ env:
1547
+ EBK_ACCEPTANCE_ARTIFACT_DIGEST: \${{ steps.resolve.outputs.artifact_digest }}
1548
+ EBK_ACCEPTANCE_ARTIFACT_ID: \${{ steps.resolve.outputs.artifact_id }}
1549
+ EBK_ACCEPTANCE_ARTIFACT_SIZE: \${{ steps.resolve.outputs.artifact_size }}
1550
+ GH_TOKEN: \${{ github.token }}
1551
+ run: >-
1552
+ node tools/ci/workspace-acceptance.mjs download
1553
+ .ebk/evidence/reused-history-acceptance
1554
+ "$EBK_ACCEPTANCE_ARTIFACT_ID"
1555
+ "$EBK_ACCEPTANCE_ARTIFACT_DIGEST"
1556
+ "$EBK_ACCEPTANCE_ARTIFACT_SIZE"
1557
+ - name: Verify exact history-only acceptance
1558
+ if: steps.resolve.outputs.reused == 'true' && steps.restore.outcome == 'success'
1559
+ id: verify
1560
+ continue-on-error: true
1561
+ env:
1562
+ EBK_ACCEPTANCE_BASE: \${{ steps.resolve.outputs.base_commit }}
1563
+ EBK_ACCEPTANCE_HEAD: \${{ steps.resolve.outputs.head_commit }}
1564
+ EBK_ACCEPTANCE_ROUTE: \${{ steps.resolve.outputs.route }}
1565
+ EBK_ACCEPTANCE_RUN_ATTEMPT: \${{ steps.resolve.outputs.run_attempt }}
1566
+ EBK_ACCEPTANCE_RUN_ID: \${{ steps.resolve.outputs.run_id }}
1567
+ run: >-
1568
+ node tools/ci/workspace-acceptance.mjs verify
1569
+ .ebk/evidence/reused-history-acceptance/manifest.json
1570
+ "$EBK_ACCEPTANCE_HEAD"
1571
+ "$EBK_ACCEPTANCE_BASE"
1572
+ "$EBK_ACCEPTANCE_ROUTE"
1573
+ "$EBK_ACCEPTANCE_RUN_ID"
1574
+ "$EBK_ACCEPTANCE_RUN_ATTEMPT"
1575
+
1576
+ deployment-mode:
1577
+ name: classify deployment profile
1578
+ runs-on: ubuntu-latest
1579
+ outputs:
1580
+ profile: \${{ steps.profile.outputs.profile }}
1581
+ steps:
1582
+ - name: Check out immutable deployment policy
1583
+ uses: ${checkoutAction}
1584
+ with:
1585
+ ref: \${{ github.sha }}
1586
+ - id: profile
1587
+ name: Read exact committed deployment profile
1588
+ run: |
1589
+ node - <<'NODE'
1590
+ const { readFileSync } = require('node:fs');
1591
+ const policy = JSON.parse(readFileSync('ebk.repository-policy.json', 'utf8'));
1592
+ const profiles = new Set(['starter', 'production-isolated', 'fully-isolated']);
1593
+ if (!profiles.has(policy.deploymentProfile)) {
1594
+ throw new Error('Committed deployment profile is absent or invalid.');
1595
+ }
1596
+ require('node:fs').appendFileSync(
1597
+ process.env.GITHUB_OUTPUT,
1598
+ 'profile=' + policy.deploymentProfile + '\\n',
1599
+ );
1600
+ NODE
1601
+
1168
1602
  route:
1603
+ name: ${protectedDeploymentRouteJobName}
1604
+ needs: [history-classification, deployment-mode]
1605
+ if: >-
1606
+ always() &&
1607
+ needs.history-classification.outputs.history_only != 'true' &&
1608
+ needs.deployment-mode.result == 'success'
1169
1609
  runs-on: ubuntu-latest
1170
1610
  steps:
1171
1611
  - name: Validate protected ref
@@ -1178,14 +1618,25 @@ jobs:
1178
1618
  esac
1179
1619
 
1180
1620
  plan-staging:
1181
- needs: route
1182
- if: github.ref == 'refs/heads/develop'
1621
+ needs: [route, deployment-mode]
1622
+ if: >-
1623
+ always() &&
1624
+ needs.route.result == 'success' &&
1625
+ needs.deployment-mode.result == 'success' &&
1626
+ github.ref == 'refs/heads/develop' &&
1627
+ (github.event_name == 'workflow_dispatch' ||
1628
+ needs.deployment-mode.outputs.profile != 'starter')
1183
1629
  runs-on: ubuntu-latest
1184
1630
  outputs:
1631
+ deployment_profile: \${{ steps.plan.outputs.deployment_profile }}
1632
+ deployment_region: \${{ steps.plan.outputs.deployment_region }}
1633
+ development_account_id: \${{ steps.plan.outputs.development_account_id }}
1185
1634
  foundation_project: \${{ steps.plan.outputs.foundation_project }}
1186
1635
  has_slices: \${{ steps.plan.outputs.has_slices }}
1187
1636
  ordered_projects: \${{ steps.plan.outputs.ordered_projects }}
1637
+ production_account_id: \${{ steps.plan.outputs.production_account_id }}
1188
1638
  slice_projects: \${{ steps.plan.outputs.slice_projects }}
1639
+ staging_account_id: \${{ steps.plan.outputs.staging_account_id }}
1189
1640
  steps:
1190
1641
  - uses: ${checkoutAction}
1191
1642
  with:
@@ -1208,7 +1659,7 @@ jobs:
1208
1659
  release-source-staging:
1209
1660
  name: select and seal staging release source
1210
1661
  needs: plan-staging
1211
- if: \${{ needs.plan-staging.result == 'success' && (github.event_name == 'push' && 'deploy' || inputs.action) != 'unlock' }}
1662
+ if: \${{ always() && needs.plan-staging.result == 'success' && (github.event_name == 'push' && 'deploy' || inputs.action) != 'unlock' }}
1212
1663
  runs-on: ubuntu-latest
1213
1664
  permissions:
1214
1665
  actions: read
@@ -1396,16 +1847,25 @@ jobs:
1396
1847
  path: .ebk/evidence/staging-release-source/**
1397
1848
 
1398
1849
  plan-production:
1399
- needs: route
1400
- if: github.ref == 'refs/heads/main'
1850
+ needs: [route, deployment-mode]
1851
+ if: >-
1852
+ always() &&
1853
+ needs.route.result == 'success' &&
1854
+ needs.deployment-mode.result == 'success' &&
1855
+ github.ref == 'refs/heads/main'
1401
1856
  runs-on: ubuntu-latest
1402
1857
  outputs:
1858
+ deployment_profile: \${{ steps.plan.outputs.deployment_profile }}
1859
+ deployment_region: \${{ steps.plan.outputs.deployment_region }}
1860
+ development_account_id: \${{ steps.plan.outputs.development_account_id }}
1403
1861
  foundation_project: \${{ steps.plan.outputs.foundation_project }}
1404
1862
  foundation_profile: \${{ steps.plan.outputs.foundation_profile }}
1405
1863
  has_slices: \${{ steps.plan.outputs.has_slices }}
1406
1864
  ordered_projects: \${{ steps.plan.outputs.ordered_projects }}
1407
1865
  production_approval_mode: \${{ steps.plan.outputs.production_approval_mode }}
1866
+ production_account_id: \${{ steps.plan.outputs.production_account_id }}
1408
1867
  slice_projects: \${{ steps.plan.outputs.slice_projects }}
1868
+ staging_account_id: \${{ steps.plan.outputs.staging_account_id }}
1409
1869
  steps:
1410
1870
  - uses: ${checkoutAction}
1411
1871
  with:
@@ -1699,7 +2159,7 @@ jobs:
1699
2159
  container-platform-staging:
1700
2160
  name: native staging OCI \${{ matrix.platform }}
1701
2161
  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' }}
2162
+ 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
2163
  strategy:
1704
2164
  fail-fast: false
1705
2165
  matrix:
@@ -1726,6 +2186,8 @@ jobs:
1726
2186
  EBK_RELEASE_SOURCE_COMMIT: \${{ needs.release-source-staging.outputs.source_commit }}
1727
2187
  EBK_SOURCE_COMMIT: \${{ needs.release-source-staging.outputs.source_commit }}
1728
2188
  EBK_STAGE: staging
2189
+ EBK_POLICY_ACCOUNT_ID: \${{ needs.plan-staging.outputs.staging_account_id }}
2190
+ EBK_POLICY_REGION: \${{ needs.plan-staging.outputs.deployment_region }}
1729
2191
  SLICE_PROJECTS: \${{ needs.plan-staging.outputs.slice_projects }}
1730
2192
  steps:
1731
2193
  - uses: ${checkoutAction}
@@ -1741,6 +2203,10 @@ jobs:
1741
2203
  - run: pnpm install --frozen-lockfile --ignore-scripts --ignore-pnpmfile
1742
2204
  - name: Verify managed workspace integrity before AWS authority
1743
2205
  run: pnpm nx run ${name}:workspace-health
2206
+ - name: Bind staging account to committed deployment topology
2207
+ run: |
2208
+ test "$AWS_EXPECTED_ACCOUNT_ID" = "$EBK_POLICY_ACCOUNT_ID"
2209
+ test "$AWS_REGION" = "$EBK_POLICY_REGION"
1744
2210
  ${stagingReleaseCandidateDownloadSteps(downloadArtifactAction)} - uses: ${configureAwsAction}
1745
2211
  with:
1746
2212
  role-to-assume: \${{ secrets.AWS_DEPLOY_ROLE_ARN }}
@@ -1780,6 +2246,8 @@ ${stagingReleaseCandidateDownloadSteps(downloadArtifactAction)} - uses: ${c
1780
2246
  EBK_RELEASE_SOURCE_COMMIT: \${{ needs.release-source-staging.outputs.source_commit }}
1781
2247
  EBK_SOURCE_COMMIT: \${{ needs.release-source-staging.outputs.source_commit }}
1782
2248
  EBK_STAGE: staging
2249
+ EBK_POLICY_ACCOUNT_ID: \${{ needs.plan-staging.outputs.staging_account_id }}
2250
+ EBK_POLICY_REGION: \${{ needs.plan-staging.outputs.deployment_region }}
1783
2251
  SLICE_PROJECTS: \${{ needs.plan-staging.outputs.slice_projects }}
1784
2252
  steps:
1785
2253
  - uses: ${checkoutAction}
@@ -1795,6 +2263,10 @@ ${stagingReleaseCandidateDownloadSteps(downloadArtifactAction)} - uses: ${c
1795
2263
  - run: pnpm install --frozen-lockfile --ignore-scripts --ignore-pnpmfile
1796
2264
  - name: Verify managed workspace integrity before AWS authority
1797
2265
  run: pnpm nx run ${name}:workspace-health
2266
+ - name: Bind staging account to committed deployment topology
2267
+ run: |
2268
+ test "$AWS_EXPECTED_ACCOUNT_ID" = "$EBK_POLICY_ACCOUNT_ID"
2269
+ test "$AWS_REGION" = "$EBK_POLICY_REGION"
1798
2270
  ${stagingReleaseCandidateDownloadSteps(downloadArtifactAction)} - name: Restore both native platform records
1799
2271
  uses: ${downloadArtifactAction}
1800
2272
  with:
@@ -1825,7 +2297,7 @@ ${stagingReleaseCandidateDownloadSteps(downloadArtifactAction)} - name: Res
1825
2297
  container-promote-production:
1826
2298
  name: promote reviewed OCI indexes to production
1827
2299
  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' }}
2300
+ if: \${{ always() && needs.plan-production.result == 'success' && needs.release-source-production.result == 'success' && needs.plan-production.outputs.slice_projects != '' }}
1829
2301
  runs-on: ubuntu-latest
1830
2302
  environment: production
1831
2303
  permissions:
@@ -1836,6 +2308,10 @@ ${stagingReleaseCandidateDownloadSteps(downloadArtifactAction)} - name: Res
1836
2308
  AWS_DEPLOY_ROLE_ARN: \${{ secrets.AWS_DEPLOY_ROLE_ARN }}
1837
2309
  AWS_EXPECTED_ACCOUNT_ID: \${{ vars.AWS_EXPECTED_ACCOUNT_ID }}
1838
2310
  AWS_REGION: \${{ vars.AWS_REGION }}
2311
+ EBK_DEPLOYMENT_PROFILE: \${{ needs.plan-production.outputs.deployment_profile }}
2312
+ EBK_POLICY_PRODUCTION_ACCOUNT_ID: \${{ needs.plan-production.outputs.production_account_id }}
2313
+ EBK_POLICY_REGION: \${{ needs.plan-production.outputs.deployment_region }}
2314
+ EBK_POLICY_STAGING_ACCOUNT_ID: \${{ needs.plan-production.outputs.staging_account_id }}
1839
2315
  EBK_CANDIDATE_RUN_ATTEMPT: \${{ needs.release-source-production.outputs.candidate_run_attempt }}
1840
2316
  EBK_CANDIDATE_RUN_ID: \${{ needs.release-source-production.outputs.candidate_run_id }}
1841
2317
  EBK_RELEASE_SOURCE_COMMIT: \${{ needs.release-source-production.outputs.source_commit }}
@@ -1936,11 +2412,13 @@ ${stagingReleaseCandidateDownloadSteps(downloadArtifactAction)} - name: Res
1936
2412
  cp "$source_sidecar" "$target"
1937
2413
  done
1938
2414
  - uses: ${configureAwsAction}
2415
+ if: env.EBK_DEPLOYMENT_PROFILE != 'starter'
1939
2416
  with:
1940
2417
  role-to-assume: \${{ secrets.AWS_STAGING_PROMOTION_PULL_ROLE_ARN }}
1941
2418
  role-session-name: ebk-staging-container-pull-\${{ github.run_id }}-\${{ github.run_attempt }}
1942
2419
  aws-region: \${{ vars.AWS_STAGING_PROMOTION_REGION }}
1943
2420
  - name: Authenticate exact staging source registry
2421
+ if: env.EBK_DEPLOYMENT_PROFILE != 'starter'
1944
2422
  env:
1945
2423
  EBK_STAGING_ACCOUNT_ID: \${{ vars.AWS_STAGING_PROMOTION_EXPECTED_ACCOUNT_ID }}
1946
2424
  EBK_STAGING_REGION: \${{ vars.AWS_STAGING_PROMOTION_REGION }}
@@ -1959,10 +2437,27 @@ ${stagingReleaseCandidateDownloadSteps(downloadArtifactAction)} - name: Res
1959
2437
  role-to-assume: \${{ secrets.AWS_DEPLOY_ROLE_ARN }}
1960
2438
  role-session-name: ebk-production-container-push-\${{ github.run_id }}-\${{ github.run_attempt }}
1961
2439
  aws-region: \${{ vars.AWS_REGION }}
2440
+ - name: Bind production authority to committed deployment topology
2441
+ run: |
2442
+ test "$AWS_EXPECTED_ACCOUNT_ID" = "$EBK_POLICY_PRODUCTION_ACCOUNT_ID" ||
2443
+ { echo 'Production environment account differs from committed deployment topology'; exit 1; }
2444
+ test "$AWS_REGION" = "$EBK_POLICY_REGION" ||
2445
+ { echo 'Production environment Region differs from committed deployment topology'; exit 1; }
2446
+ - name: Authenticate shared-account staging source registry
2447
+ if: env.EBK_DEPLOYMENT_PROFILE == 'starter'
2448
+ run: |
2449
+ test "$EBK_POLICY_STAGING_ACCOUNT_ID" = "$EBK_POLICY_PRODUCTION_ACCOUNT_ID" ||
2450
+ { echo 'Starter promotion requires one shared AWS account'; exit 1; }
2451
+ registry_suffix=.com
2452
+ case "$EBK_POLICY_REGION" in cn-*) registry_suffix=.com.cn ;; esac
2453
+ aws ecr get-login-password --region "$EBK_POLICY_REGION" |
2454
+ docker login --username AWS --password-stdin \
2455
+ "$EBK_POLICY_STAGING_ACCOUNT_ID.dkr.ecr.$EBK_POLICY_REGION.amazonaws$registry_suffix"
1962
2456
  - name: Promote exact staging indexes without rebuilding
1963
2457
  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 }}
2458
+ EBK_DEPLOYMENT_PROFILE: \${{ needs.plan-production.outputs.deployment_profile }}
2459
+ 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 }}
2460
+ EBK_STAGING_PROMOTION_REGION: \${{ needs.plan-production.outputs.deployment_profile == 'starter' && needs.plan-production.outputs.deployment_region || vars.AWS_STAGING_PROMOTION_REGION }}
1966
2461
  run: |
1967
2462
  test "$(aws sts get-caller-identity --query Account --output text)" = "$AWS_EXPECTED_ACCOUNT_ID" ||
1968
2463
  { echo 'Production promotion role account differs from policy'; exit 1; }
@@ -1995,7 +2490,7 @@ ${planRoleAuditJob('production', configureAwsAction)}${planRoleAuditJob('staging
1995
2490
  (
1996
2491
  needs.release-source-production.result == 'success' &&
1997
2492
  (
1998
- needs.plan-production.outputs.has_slices != 'true' ||
2493
+ needs.plan-production.outputs.slice_projects == '' ||
1999
2494
  needs.container-promote-production.result == 'success'
2000
2495
  )
2001
2496
  )
@@ -2089,7 +2584,7 @@ ${infrastructureCandidateDownloadSteps(downloadArtifactAction, {
2089
2584
  runId: '${{ needs.release-source-production.outputs.candidate_run_id }}',
2090
2585
  verifiedStagingRunId: '${{ needs.release-source-production.outputs.verified_staging_run_id }}',
2091
2586
  })} - name: Restore exact production container bindings
2092
- if: env.EBK_ACTION == 'deploy' && needs.plan-production.outputs.has_slices == 'true'
2587
+ if: env.EBK_ACTION == 'deploy' && needs.plan-production.outputs.slice_projects != ''
2093
2588
  env:
2094
2589
  ACTIONS_ID_TOKEN_REQUEST_TOKEN: ''
2095
2590
  ACTIONS_ID_TOKEN_REQUEST_URL: ''
@@ -2150,7 +2645,7 @@ ${planRoleAuditStep('main', 'verify')}
2150
2645
  (github.event_name == 'push' && 'deploy' || inputs.action) != 'unlock' &&
2151
2646
  (
2152
2647
  (github.event_name == 'push' && 'deploy' || inputs.action) != 'deploy' ||
2153
- needs.plan-staging.outputs.has_slices != 'true' ||
2648
+ needs.plan-staging.outputs.slice_projects == '' ||
2154
2649
  needs.container-assemble-staging.result == 'success'
2155
2650
  )
2156
2651
  runs-on: ubuntu-latest
@@ -2181,6 +2676,8 @@ ${planRoleAuditStep('main', 'verify')}
2181
2676
  EBK_RELEASE_SOURCE_COMMIT: \${{ needs.release-source-staging.outputs.source_commit }}
2182
2677
  EBK_SOURCE_COMMIT: \${{ github.sha }}
2183
2678
  EBK_STAGE: staging
2679
+ EBK_POLICY_ACCOUNT_ID: \${{ needs.plan-staging.outputs.staging_account_id }}
2680
+ EBK_POLICY_REGION: \${{ needs.plan-staging.outputs.deployment_region }}
2184
2681
  FOUNDATION_PROJECT: \${{ needs.plan-staging.outputs.foundation_project }}
2185
2682
  ORDERED_PROJECTS: \${{ needs.plan-staging.outputs.ordered_projects }}
2186
2683
  SLICE_PROJECTS: \${{ needs.plan-staging.outputs.slice_projects }}
@@ -2238,7 +2735,7 @@ ${planApplicationBindingCheck()} case "$AWS_PLAN_ROLE_ARN" in
2238
2735
  ACTIONS_ID_TOKEN_REQUEST_URL: ''
2239
2736
  run: pnpm nx run ${name}:workspace-health
2240
2737
  ${stagingReleaseCandidateDownloadSteps(downloadArtifactAction)} - name: Restore exact staging container bindings
2241
- if: env.EBK_ACTION == 'deploy' && needs.plan-staging.outputs.has_slices == 'true'
2738
+ if: env.EBK_ACTION == 'deploy' && needs.plan-staging.outputs.slice_projects != ''
2242
2739
  env:
2243
2740
  ACTIONS_ID_TOKEN_REQUEST_TOKEN: ''
2244
2741
  ACTIONS_ID_TOKEN_REQUEST_URL: ''
@@ -2288,14 +2785,14 @@ ${planRoleAuditStep('develop', 'verify')}
2288
2785
  packages/*/.ebk/evidence/infrastructure-plan-command.json
2289
2786
 
2290
2787
  deploy-staging:
2291
- name: apply staging plan \${{ needs.infrastructure-plan-staging.outputs.plan_digest || 'recovery-without-resource-plan' }}
2788
+ name: ${stagingApplyJobPrefix}\${{ needs.infrastructure-plan-staging.outputs.plan_digest || 'recovery-without-resource-plan' }}
2292
2789
  needs: [plan-staging, release-source-staging, container-assemble-staging, infrastructure-plan-staging]
2293
2790
  if: |
2294
2791
  always() &&
2295
2792
  needs.plan-staging.result == 'success' &&
2296
2793
  (
2297
2794
  (github.event_name == 'push' && 'deploy' || inputs.action) != 'deploy' ||
2298
- needs.plan-staging.outputs.has_slices != 'true' ||
2795
+ needs.plan-staging.outputs.slice_projects == '' ||
2299
2796
  needs.container-assemble-staging.result == 'success'
2300
2797
  ) &&
2301
2798
  (
@@ -2357,7 +2854,7 @@ ${stagingReleaseCandidateDownloadSteps(downloadArtifactAction)} - name: Res
2357
2854
  name: infrastructure-plan-staging-\${{ github.run_id }}-\${{ github.run_attempt }}
2358
2855
  path: .
2359
2856
  - name: Restore exact staging container bindings
2360
- if: env.EBK_ACTION == 'deploy' && needs.plan-staging.outputs.has_slices == 'true'
2857
+ if: env.EBK_ACTION == 'deploy' && needs.plan-staging.outputs.slice_projects != ''
2361
2858
  uses: ${downloadArtifactAction}
2362
2859
  with:
2363
2860
  name: container-release-staging-\${{ github.run_id }}-\${{ github.run_attempt }}
@@ -2367,6 +2864,10 @@ ${stagingReleaseCandidateDownloadSteps(downloadArtifactAction)} - name: Res
2367
2864
  test -n "$AWS_DEPLOY_ROLE_ARN" || { echo 'staging AWS_DEPLOY_ROLE_ARN is required'; exit 1; }
2368
2865
  test -n "$AWS_EXPECTED_ACCOUNT_ID" || { echo 'staging AWS_EXPECTED_ACCOUNT_ID is required'; exit 1; }
2369
2866
  test -n "$AWS_REGION" || { echo 'staging AWS_REGION is required'; exit 1; }
2867
+ test "$AWS_EXPECTED_ACCOUNT_ID" = "$EBK_POLICY_ACCOUNT_ID" ||
2868
+ { echo 'staging environment account differs from committed deployment topology'; exit 1; }
2869
+ test "$AWS_REGION" = "$EBK_POLICY_REGION" ||
2870
+ { echo 'staging environment Region differs from committed deployment topology'; exit 1; }
2370
2871
  test -n "$EBK_FLEET_ORIGIN" ||
2371
2872
  { echo 'staging environment EBK_FLEET_ORIGIN is required'; exit 1; }
2372
2873
  test "$EBK_FLEET_ORIGIN" = "$EBK_PLANNED_FLEET_ORIGIN" ||
@@ -2378,8 +2879,7 @@ ${stagingReleaseCandidateDownloadSteps(downloadArtifactAction)} - name: Res
2378
2879
  test -n "$AWS_PLAN_GENERATED_SECRET_ARNS_JSON" &&
2379
2880
  test -n "$AWS_PLAN_PROVIDER_REFRESH_POLICY_JSON" ||
2380
2881
  { 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" ||
2882
+ test -n "$AWS_PLAN_ROLE_ARN" ||
2383
2883
  { echo 'staging exact plan-authority inputs are required'; exit 1; }
2384
2884
  case "$AWS_DEPLOY_ROLE_ARN" in
2385
2885
  arn:aws:iam::$AWS_EXPECTED_ACCOUNT_ID:role/*) ;;
@@ -2946,7 +3446,7 @@ ${stagingReleaseCandidateDownloadSteps(downloadArtifactAction)} - name: Res
2946
3446
  needs.plan-production.result == 'success' &&
2947
3447
  (
2948
3448
  (github.event_name == 'push' && 'deploy' || inputs.action) != 'deploy' ||
2949
- needs.plan-production.outputs.has_slices != 'true' ||
3449
+ needs.plan-production.outputs.slice_projects == '' ||
2950
3450
  needs.container-promote-production.result == 'success'
2951
3451
  ) &&
2952
3452
  (
@@ -3003,6 +3503,8 @@ ${stagingReleaseCandidateDownloadSteps(downloadArtifactAction)} - name: Res
3003
3503
  EBK_APPROVED_MAIN_COMMIT: \${{ github.sha }}
3004
3504
  EBK_SOURCE_COMMIT: \${{ github.sha }}
3005
3505
  EBK_STAGE: production
3506
+ EBK_POLICY_ACCOUNT_ID: \${{ needs.plan-production.outputs.production_account_id }}
3507
+ EBK_POLICY_REGION: \${{ needs.plan-production.outputs.deployment_region }}
3006
3508
  EBK_VERIFIED_STAGING_RUN_ID: \${{ needs.release-source-production.outputs.verified_staging_run_id }}
3007
3509
  FOUNDATION_PROJECT: \${{ needs.plan-production.outputs.foundation_project }}
3008
3510
  ORDERED_PROJECTS: \${{ needs.plan-production.outputs.ordered_projects }}
@@ -3089,7 +3591,7 @@ ${stagingReleaseCandidateDownloadSteps(downloadArtifactAction)} - name: Res
3089
3591
  name: infrastructure-plan-production-\${{ github.run_id }}-\${{ github.run_attempt }}
3090
3592
  path: .
3091
3593
  - name: Restore exact production container bindings
3092
- if: env.EBK_ACTION == 'deploy' && needs.plan-production.outputs.has_slices == 'true'
3594
+ if: env.EBK_ACTION == 'deploy' && needs.plan-production.outputs.slice_projects != ''
3093
3595
  uses: ${downloadArtifactAction}
3094
3596
  with:
3095
3597
  name: container-release-production-\${{ github.run_id }}-\${{ github.run_attempt }}
@@ -3099,6 +3601,10 @@ ${stagingReleaseCandidateDownloadSteps(downloadArtifactAction)} - name: Res
3099
3601
  test -n "$AWS_DEPLOY_ROLE_ARN" || { echo 'production AWS_DEPLOY_ROLE_ARN is required'; exit 1; }
3100
3602
  test -n "$AWS_EXPECTED_ACCOUNT_ID" || { echo 'production AWS_EXPECTED_ACCOUNT_ID is required'; exit 1; }
3101
3603
  test -n "$AWS_REGION" || { echo 'production AWS_REGION is required'; exit 1; }
3604
+ test "$AWS_EXPECTED_ACCOUNT_ID" = "$EBK_POLICY_ACCOUNT_ID" ||
3605
+ { echo 'production environment account differs from committed deployment topology'; exit 1; }
3606
+ test "$AWS_REGION" = "$EBK_POLICY_REGION" ||
3607
+ { echo 'production environment Region differs from committed deployment topology'; exit 1; }
3102
3608
  test -n "$EBK_FLEET_ORIGIN" ||
3103
3609
  { echo 'production environment EBK_FLEET_ORIGIN is required'; exit 1; }
3104
3610
  test "$EBK_FLEET_ORIGIN" = "$EBK_PLANNED_FLEET_ORIGIN" ||
@@ -3110,8 +3616,7 @@ ${stagingReleaseCandidateDownloadSteps(downloadArtifactAction)} - name: Res
3110
3616
  test -n "$AWS_PLAN_GENERATED_SECRET_ARNS_JSON" &&
3111
3617
  test -n "$AWS_PLAN_PROVIDER_REFRESH_POLICY_JSON" ||
3112
3618
  { 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" ||
3619
+ test -n "$AWS_PLAN_ROLE_ARN" ||
3115
3620
  { echo 'production exact plan-authority inputs are required'; exit 1; }
3116
3621
  case "$AWS_DEPLOY_ROLE_ARN" in
3117
3622
  arn:aws:iam::$AWS_EXPECTED_ACCOUNT_ID:role/*) ;;
@@ -3492,7 +3997,7 @@ on:
3492
3997
  description: Exact protected deployment stage
3493
3998
  required: true
3494
3999
  type: choice
3495
- options: [staging, production]
4000
+ options: [${ecrInspectorWorkflowStageChoices}]
3496
4001
  deployment_commit:
3497
4002
  description: Exact successful protected deployment commit
3498
4003
  required: true
@@ -3501,8 +4006,8 @@ on:
3501
4006
  description: Exact successful Deploy workflow run ID
3502
4007
  required: true
3503
4008
  type: string
3504
- release_artifact_name:
3505
- description: Exact release-evidence artifact from that run
4009
+ deployment_receipt_artifact_name:
4010
+ description: Exact deployment-receipt artifact from that run
3506
4011
  required: true
3507
4012
  type: string
3508
4013
  slice:
@@ -3556,7 +4061,7 @@ jobs:
3556
4061
  EBK_DEPLOYMENT_ARN: \${{ inputs.deployment_arn }}
3557
4062
  EBK_DEPLOYMENT_KIND: \${{ inputs.deployment_kind }}
3558
4063
  EBK_DEPLOY_RUN_ID: \${{ inputs.deploy_run_id }}
3559
- EBK_RELEASE_ARTIFACT_NAME: \${{ inputs.release_artifact_name }}
4064
+ EBK_DEPLOYMENT_RECEIPT_ARTIFACT_NAME: \${{ inputs.deployment_receipt_artifact_name }}
3560
4065
  EBK_SLICE: \${{ inputs.slice }}
3561
4066
  EBK_DEPLOYMENT_COMMIT: \${{ inputs.deployment_commit }}
3562
4067
  EBK_STAGE: \${{ inputs.stage }}
@@ -3579,10 +4084,10 @@ jobs:
3579
4084
  esac
3580
4085
  expected_branch=develop
3581
4086
  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 ;;
4087
+ expected_artifact="deployment-receipt-$EBK_STAGE-$EBK_DEPLOY_RUN_ID-"
4088
+ case "$EBK_DEPLOYMENT_RECEIPT_ARTIFACT_NAME" in
4089
+ "$expected_artifact"*) attempt="\${EBK_DEPLOYMENT_RECEIPT_ARTIFACT_NAME#"$expected_artifact"}" ;;
4090
+ *) echo 'Deployment receipt artifact does not bind the exact deploy run'; exit 1 ;;
3586
4091
  esac
3587
4092
  case "$attempt" in
3588
4093
  ''|0*|*[!0-9]*) echo 'Release evidence artifact has an invalid run attempt'; exit 1 ;;
@@ -3595,7 +4100,7 @@ jobs:
3595
4100
  test "$(printf '%s' "$run" | jq -r .path)" = ".github/workflows/deploy.yml" ||
3596
4101
  { echo 'Deploy run is not the exact successful protected source authority'; exit 1; }
3597
4102
  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" \
4103
+ count="$(printf '%s' "$artifacts" | jq --arg name "$EBK_DEPLOYMENT_RECEIPT_ARTIFACT_NAME" \
3599
4104
  '[.artifacts[] | select(.name == $name and .expired == false)] | length')"
3600
4105
  test "$count" = 1 ||
3601
4106
  { echo 'Exact unexpired release evidence artifact is absent or ambiguous'; exit 1; }
@@ -3612,7 +4117,7 @@ jobs:
3612
4117
  - uses: ${downloadArtifactAction}
3613
4118
  with:
3614
4119
  github-token: \${{ github.token }}
3615
- name: \${{ inputs.release_artifact_name }}
4120
+ name: \${{ inputs.deployment_receipt_artifact_name }}
3616
4121
  path: .
3617
4122
  run-id: \${{ inputs.deploy_run_id }}
3618
4123
  - uses: ${pnpmAction}
@@ -3719,7 +4224,7 @@ jobs:
3719
4224
  { encoding: "utf8" },
3720
4225
  ).trim()
3721
4226
  ) fail("Production deployment tree differs from its reviewed release source.");
3722
- } else if (stage !== "staging") {
4227
+ } else if (stage !== "${rehearsalStage}") {
3723
4228
  fail("Protected release stage is invalid.");
3724
4229
  }
3725
4230
  const productionKeys = [
@@ -3775,7 +4280,7 @@ jobs:
3775
4280
  !digest.test(binding.candidateDigest ?? "") ||
3776
4281
  (stage === "production" &&
3777
4282
  !digest.test(binding.lifecycleDigest ?? "")) ||
3778
- (stage === "staging" && binding.stage !== stage) ||
4283
+ (stage === "${rehearsalStage}" && binding.stage !== stage) ||
3779
4284
  binding.runId !== process.env.EBK_DEPLOY_RUN_ID ||
3780
4285
  binding.runAttempt !== process.env.EBK_DEPLOY_RUN_ATTEMPT ||
3781
4286
  binding.workflow !== expectedWorkflow
@@ -3854,7 +4359,7 @@ on:
3854
4359
  description: Protected deployed stage
3855
4360
  required: true
3856
4361
  type: choice
3857
- options: [staging, production]
4362
+ options: [${protectedWorkflowStageChoices}]
3858
4363
  project:
3859
4364
  description: Exact Product Slice project
3860
4365
  required: true
@@ -3954,7 +4459,7 @@ jobs:
3954
4459
  EBK_REPORT_RUN_ID: \${{ inputs.report_run_id }}
3955
4460
  run: |
3956
4461
  case "$EBK_STAGE:$EBK_PROTECTED_REF" in
3957
- staging:refs/heads/develop|production:refs/heads/main) ;;
4462
+ ${protectedWorkflowStageBranchCases}) ;;
3958
4463
  *) echo 'Async recovery stage does not match its protected branch'; exit 1 ;;
3959
4464
  esac
3960
4465
  case "$EBK_PROJECT" in
@@ -4765,13 +5270,78 @@ permissions:
4765
5270
  contents: read
4766
5271
 
4767
5272
  jobs:
5273
+ deployment-classification:
5274
+ name: classify exact staging deployment
5275
+ if: >-
5276
+ github.event_name == 'workflow_run' &&
5277
+ github.event.workflow_run.event == 'push' &&
5278
+ github.event.workflow_run.head_branch == 'develop' &&
5279
+ github.event.workflow_run.conclusion == 'success'
5280
+ permissions:
5281
+ actions: read
5282
+ contents: read
5283
+ outputs:
5284
+ deployed: \${{ steps.classify.outputs.deployed }}
5285
+ runs-on: ubuntu-latest
5286
+ steps:
5287
+ - name: Check out immutable deployment policy
5288
+ uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
5289
+ with:
5290
+ persist-credentials: false
5291
+ ref: \${{ github.event.workflow_run.head_sha }}
5292
+ - id: profile
5293
+ name: Read exact committed deployment profile
5294
+ run: |
5295
+ node - <<'NODE'
5296
+ const { appendFileSync, readFileSync } = require('node:fs');
5297
+ const policy = JSON.parse(
5298
+ readFileSync('ebk.repository-policy.json', 'utf8'),
5299
+ );
5300
+ const profiles = new Set([
5301
+ 'starter',
5302
+ 'production-isolated',
5303
+ 'fully-isolated',
5304
+ ]);
5305
+ if (!profiles.has(policy.deploymentProfile)) {
5306
+ throw new Error('Committed deployment profile is absent or invalid.');
5307
+ }
5308
+ appendFileSync(
5309
+ process.env.GITHUB_OUTPUT,
5310
+ 'profile=' + policy.deploymentProfile + '\\n',
5311
+ );
5312
+ NODE
5313
+ - id: classify
5314
+ name: Require one successful thin staging deployment job
5315
+ env:
5316
+ EBK_DEPLOYMENT_PROFILE: \${{ steps.profile.outputs.profile }}
5317
+ EBK_DEPLOYMENT_RUN_ID: \${{ github.event.workflow_run.id }}
5318
+ GH_TOKEN: \${{ github.token }}
5319
+ GH_REPO: \${{ github.repository }}
5320
+ run: |
5321
+ if [ "$EBK_DEPLOYMENT_PROFILE" = starter ]; then
5322
+ echo 'deployed=false' >> "$GITHUB_OUTPUT"
5323
+ exit 0
5324
+ fi
5325
+ jobs="$(gh api \
5326
+ "/repos/$GH_REPO/actions/runs/$EBK_DEPLOYMENT_RUN_ID/jobs?filter=latest&per_page=100")"
5327
+ execute_count="$(printf '%s' "$jobs" | jq \
5328
+ '[.jobs[] | select(.name == "native deploy staging" and .conclusion == "success")] | length')"
5329
+ [ "$execute_count" = 1 ] || {
5330
+ echo 'Deploy must contain exactly one successful thin staging execution'
5331
+ exit 1
5332
+ }
5333
+ echo 'deployed=true' >> "$GITHUB_OUTPUT"
5334
+
4768
5335
  plan:
5336
+ needs: deployment-classification
4769
5337
  if: >-
4770
- (github.event_name == 'workflow_run' &&
5338
+ always() &&
5339
+ ((github.event_name == 'workflow_run' &&
4771
5340
  github.event.workflow_run.conclusion == 'success' &&
4772
5341
  github.event.workflow_run.event == 'push' &&
4773
- github.event.workflow_run.head_branch == 'develop') ||
4774
- github.event_name == 'workflow_dispatch'
5342
+ github.event.workflow_run.head_branch == 'develop' &&
5343
+ needs.deployment-classification.outputs.deployed == 'true') ||
5344
+ github.event_name == 'workflow_dispatch')
4775
5345
  outputs:
4776
5346
  deployment_run_attempt: \${{ steps.identity.outputs.deployment_run_attempt }}
4777
5347
  deployment_run_id: \${{ steps.identity.outputs.deployment_run_id }}
@@ -4878,9 +5448,10 @@ jobs:
4878
5448
  EBK_SMOKE_AUTHORITY_VERSION: \${{ vars.EBK_SMOKE_AUTHORITY_VERSION }}
4879
5449
  EBK_SMOKE_TARGETS: \${{ vars.EBK_SMOKE_TARGETS }}
4880
5450
  EBK_SOURCE_COMMIT: \${{ needs.plan.outputs.source_commit }}
4881
- PLAYWRIGHT_BROWSERS_PATH: \${{ runner.temp }}/ebk-playwright-browsers
4882
5451
  runs-on: ubuntu-latest
4883
5452
  steps:
5453
+ - name: Initialize private browser cache path
5454
+ run: echo "PLAYWRIGHT_BROWSERS_PATH=$RUNNER_TEMP/ebk-playwright-browsers" >> "$GITHUB_ENV"
4884
5455
  - name: Validate credential-free preparation
4885
5456
  run: |
4886
5457
  test -n "$AWS_EXPECTED_ACCOUNT_ID" || { echo 'AWS_EXPECTED_ACCOUNT_ID is required'; exit 1; }
@@ -5264,14 +5835,14 @@ jobs:
5264
5835
  test "$(node --version | cut -d. -f1)" = v22
5265
5836
  - name: Run exact deployed smoke
5266
5837
  working-directory: \${{ runner.temp }}/ebk-prepared-workspace
5267
- run: node node_modules/nx/bin/nx.js run "$EBK_PROJECT:smoke-deployed"
5838
+ run: node node_modules/nx/dist/bin/nx.js run "$EBK_PROJECT:smoke-deployed"
5268
5839
  - name: Verify reviewed frontend visual baseline
5269
5840
  working-directory: \${{ runner.temp }}/ebk-prepared-workspace
5270
- run: node node_modules/nx/bin/nx.js run "$EBK_PROJECT:frontend-visual"
5841
+ run: node node_modules/nx/dist/bin/nx.js run "$EBK_PROJECT:frontend-visual"
5271
5842
  - name: Verify deployed frontend security headers
5272
5843
  working-directory: \${{ runner.temp }}/ebk-prepared-workspace
5273
5844
  run: |
5274
- node node_modules/nx/bin/nx.js run \
5845
+ node node_modules/nx/dist/bin/nx.js run \
5275
5846
  "$EBK_PROJECT:frontend-security-headers" -- \
5276
5847
  --url="$EBK_SMOKE_URL"
5277
5848
  - name: Bind exact alarm and data-integrity evidence
@@ -6642,7 +7213,7 @@ on:
6642
7213
  description: Protected stage
6643
7214
  required: true
6644
7215
  type: choice
6645
- options: [staging, production]
7216
+ options: [${protectedWorkflowStageChoices}]
6646
7217
  project:
6647
7218
  description: Exact Product Slice project
6648
7219
  required: true
@@ -6668,11 +7239,9 @@ jobs:
6668
7239
  contents: read
6669
7240
  id-token: write
6670
7241
  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
7242
  EBK_ACTION: \${{ inputs.action }}
6675
7243
  EBK_PROJECT: \${{ inputs.project }}
7244
+ EBK_RENDERED_PROFILE: ${deploymentProfile}
6676
7245
  EBK_STAGE: \${{ inputs.stage }}
6677
7246
  runs-on: ubuntu-latest
6678
7247
  steps:
@@ -6681,19 +7250,12 @@ jobs:
6681
7250
  EBK_PROTECTED_REF: \${{ github.ref }}
6682
7251
  run: |
6683
7252
  case "$EBK_STAGE:$EBK_PROTECTED_REF" in
6684
- staging:refs/heads/develop|production:refs/heads/main) ;;
7253
+ ${protectedWorkflowStageBranchCases}) ;;
6685
7254
  *) echo 'Secret metadata stage does not match the protected branch'; exit 1 ;;
6686
7255
  esac
6687
7256
  case "$EBK_PROJECT" in
6688
7257
  ''|-*|*-|*[!a-z0-9-]*) echo 'Secret metadata project must be an exact normalized Slice name'; exit 1 ;;
6689
7258
  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
7259
  - uses: ${checkoutAction}
6698
7260
  with:
6699
7261
  persist-credentials: false
@@ -6705,24 +7267,153 @@ jobs:
6705
7267
  - run: pnpm install --frozen-lockfile --ignore-scripts --ignore-pnpmfile
6706
7268
  - name: Verify managed workspace integrity before AWS authority
6707
7269
  run: pnpm nx run ${name}:workspace-health
7270
+ - id: policy
7271
+ name: Derive committed secret metadata stage policy
7272
+ run: |
7273
+ ${indentWorkflowScript(protectedSecretPolicyDerivationScript(), 10)}
7274
+ - id: authority
7275
+ name: Derive canonical committed stage authority
7276
+ env:
7277
+ EBK_DEVELOPMENT_ACCOUNT: \${{ steps.policy.outputs.development_account_id }}
7278
+ EBK_PRODUCTION_ACCOUNT: \${{ steps.policy.outputs.production_account_id }}
7279
+ EBK_REGION: \${{ steps.policy.outputs.deployment_region }}
7280
+ EBK_STAGING_ACCOUNT: \${{ steps.policy.outputs.staging_account_id }}
7281
+ run: |
7282
+ ${indentWorkflowScript(`set -euo pipefail\n${(0, workspace_files_thin_deploy_js_1.canonicalStageAuthorityDerivationScript)()}`, 10)}
6708
7283
  - uses: ${configureAwsAction}
6709
7284
  with:
6710
- role-to-assume: \${{ secrets.AWS_DEPLOY_ROLE_ARN }}
7285
+ audience: \${{ steps.authority.outputs.audience }}
7286
+ role-to-assume: \${{ steps.authority.outputs.role_arn }}
6711
7287
  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"
7288
+ aws-region: \${{ steps.policy.outputs.deployment_region }}
7289
+ - name: Verify exact stage authority
7290
+ env:
7291
+ AWS_DEPLOY_ROLE_ARN: \${{ steps.authority.outputs.role_arn }}
7292
+ AWS_PARTITION: \${{ steps.authority.outputs.partition }}
7293
+ EBK_POLICY_ACCOUNT_ID: \${{ steps.authority.outputs.account_id }}
7294
+ run: |
7295
+ ${indentWorkflowScript((0, workspace_files_thin_deploy_js_1.exactStageDeployerGuardScript)('Secret metadata', '$EBK_POLICY_ACCOUNT_ID'), 10)}
6715
7296
  - name: Reconcile value-blind metadata
7297
+ env:
7298
+ AWS_DEPLOY_ROLE_ARN: \${{ steps.authority.outputs.role_arn }}
7299
+ AWS_EXPECTED_ACCOUNT_ID: \${{ steps.authority.outputs.account_id }}
7300
+ AWS_REGION: \${{ steps.policy.outputs.deployment_region }}
6716
7301
  run: |
6717
7302
  args=("--stage=$EBK_STAGE" "--region=$AWS_REGION")
6718
7303
  if [ "$EBK_ACTION" = apply ]; then args+=("--apply"); fi
6719
7304
  pnpm nx run "$EBK_PROJECT:secret-metadata" -- "\${args[@]}"
6720
7305
  `,
6721
- '.github/workflows/sync-back.yml': `name: Sync production history
7306
+ '.github/workflows/secret-initialize.yml': `name: Secret initialize
7307
+
7308
+ on:
7309
+ workflow_dispatch:
7310
+ inputs:
7311
+ stage:
7312
+ description: Protected stage
7313
+ required: true
7314
+ type: choice
7315
+ options: [${protectedWorkflowStageChoices}]
7316
+ project:
7317
+ description: Exact Product Slice project
7318
+ required: true
7319
+ type: string
7320
+ logical_name:
7321
+ description: Exact machine-generated secret logical name
7322
+ required: true
7323
+ type: string
7324
+ action:
7325
+ description: Value-blind initialization action
7326
+ required: true
7327
+ default: report
7328
+ type: choice
7329
+ options: [report, apply]
7330
+
7331
+ concurrency:
7332
+ group: secret-initialize-\${{ inputs.stage }}-\${{ inputs.project }}-\${{ inputs.logical_name }}
7333
+ cancel-in-progress: false
7334
+
7335
+ permissions:
7336
+ contents: read
7337
+
7338
+ jobs:
7339
+ initialize:
7340
+ environment: \${{ inputs.stage }}
7341
+ permissions:
7342
+ contents: read
7343
+ id-token: write
7344
+ env:
7345
+ EBK_ACTION: \${{ inputs.action }}
7346
+ EBK_LOGICAL_NAME: \${{ inputs.logical_name }}
7347
+ EBK_PROJECT: \${{ inputs.project }}
7348
+ EBK_RENDERED_PROFILE: ${deploymentProfile}
7349
+ EBK_STAGE: \${{ inputs.stage }}
7350
+ runs-on: ubuntu-latest
7351
+ steps:
7352
+ - name: Validate protected request
7353
+ env:
7354
+ EBK_PROTECTED_REF: \${{ github.ref }}
7355
+ run: |
7356
+ case "$EBK_STAGE:$EBK_PROTECTED_REF" in
7357
+ ${protectedWorkflowStageBranchCases}) ;;
7358
+ *) echo 'Secret initialize stage does not match the protected branch'; exit 1 ;;
7359
+ esac
7360
+ case "$EBK_PROJECT" in
7361
+ ''|-*|*-|*[!a-z0-9-]*) echo 'Secret initialize project must be an exact normalized Slice name'; exit 1 ;;
7362
+ esac
7363
+ [[ "$EBK_LOGICAL_NAME" =~ ^[A-Za-z][A-Za-z0-9]{0,63}$ ]] ||
7364
+ { echo 'Secret initialize logical name is invalid'; exit 1; }
7365
+ - uses: ${checkoutAction}
7366
+ with:
7367
+ persist-credentials: false
7368
+ - uses: ${pnpmAction}
7369
+ - uses: ${setupNodeAction}
7370
+ with:
7371
+ node-version: ${tool_versions_js_1.WORKSPACE_NODE_VERSION}
7372
+ cache: pnpm
7373
+ - run: pnpm install --frozen-lockfile --ignore-scripts --ignore-pnpmfile
7374
+ - name: Verify managed workspace integrity before AWS authority
7375
+ run: pnpm nx run ${name}:workspace-health
7376
+ - id: policy
7377
+ name: Derive committed secret initialize stage policy
7378
+ run: |
7379
+ ${indentWorkflowScript(protectedSecretPolicyDerivationScript(), 10)}
7380
+ - id: authority
7381
+ name: Derive canonical committed stage authority
7382
+ env:
7383
+ EBK_DEVELOPMENT_ACCOUNT: \${{ steps.policy.outputs.development_account_id }}
7384
+ EBK_PRODUCTION_ACCOUNT: \${{ steps.policy.outputs.production_account_id }}
7385
+ EBK_REGION: \${{ steps.policy.outputs.deployment_region }}
7386
+ EBK_STAGING_ACCOUNT: \${{ steps.policy.outputs.staging_account_id }}
7387
+ run: |
7388
+ ${indentWorkflowScript(`set -euo pipefail\n${(0, workspace_files_thin_deploy_js_1.canonicalStageAuthorityDerivationScript)()}`, 10)}
7389
+ - uses: ${configureAwsAction}
7390
+ with:
7391
+ audience: \${{ steps.authority.outputs.audience }}
7392
+ role-to-assume: \${{ steps.authority.outputs.role_arn }}
7393
+ role-session-name: ebk-secret-initialize-\${{ github.run_id }}-\${{ github.run_attempt }}
7394
+ aws-region: \${{ steps.policy.outputs.deployment_region }}
7395
+ - name: Verify exact stage authority
7396
+ env:
7397
+ AWS_DEPLOY_ROLE_ARN: \${{ steps.authority.outputs.role_arn }}
7398
+ AWS_PARTITION: \${{ steps.authority.outputs.partition }}
7399
+ EBK_POLICY_ACCOUNT_ID: \${{ steps.authority.outputs.account_id }}
7400
+ run: |
7401
+ ${indentWorkflowScript((0, workspace_files_thin_deploy_js_1.exactStageDeployerGuardScript)('Secret initialize', '$EBK_POLICY_ACCOUNT_ID'), 10)}
7402
+ - name: Initialize machine-generated secret value-blind
7403
+ env:
7404
+ AWS_DEPLOY_ROLE_ARN: \${{ steps.authority.outputs.role_arn }}
7405
+ AWS_EXPECTED_ACCOUNT_ID: \${{ steps.authority.outputs.account_id }}
7406
+ AWS_REGION: \${{ steps.policy.outputs.deployment_region }}
7407
+ run: |
7408
+ args=("--logicalName=$EBK_LOGICAL_NAME" "--stage=$EBK_STAGE" "--region=$AWS_REGION")
7409
+ if [ "$EBK_ACTION" = apply ]; then args+=("--apply"); fi
7410
+ pnpm nx run "$EBK_PROJECT:secret-initialize" -- "\${args[@]}"
7411
+ `,
7412
+ '.github/workflows/sync-back.yml': `name: Sync protected main history
6722
7413
 
6723
7414
  on:
6724
7415
  workflow_run:
6725
- workflows: [Deploy]
7416
+ workflows: [Verify]
6726
7417
  types: [completed]
6727
7418
 
6728
7419
  permissions:
@@ -6731,8 +7422,12 @@ permissions:
6731
7422
 
6732
7423
  jobs:
6733
7424
  sync-back:
7425
+ # The sync carries history, not deployment evidence: open it after any
7426
+ # completed main run so a failed production deploy cannot lock develop
7427
+ # behind a sync that never opens.
6734
7428
  if: >-
6735
- github.event.workflow_run.conclusion == 'success' &&
7429
+ github.event.workflow_run.conclusion != 'cancelled' &&
7430
+ github.event.workflow_run.event == 'push' &&
6736
7431
  github.event.workflow_run.head_branch == 'main'
6737
7432
  runs-on: ubuntu-latest
6738
7433
  steps:
@@ -6746,8 +7441,8 @@ jobs:
6746
7441
  gh pr create \
6747
7442
  --head main \
6748
7443
  --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.'
7444
+ --title 'chore(git): sync protected main to develop' \
7445
+ --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
7446
  number=$(gh pr list --state open --head main --base develop --json number --jq '.[0].number')
6752
7447
  fi
6753
7448
  echo "Sync-back pull request #$number is ready for review."
@@ -7005,9 +7700,16 @@ infrastructure or protected deployment policy. Preserve the ownership boundary r
7005
7700
  \`.ebk/ownership.json\`: managed files belong to EBK, seeded files are generated starting
7006
7701
  points that adopters may change, and unrecorded files are adopter-owned.
7007
7702
 
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.
7703
+ Ordinary feature work uses affected tests, typecheck, lint, and ownership checks.
7704
+ At a local milestone, \`pnpm verify\` checks current source through
7705
+ \`${name}:verify-proposed\`, including before the first Git commit. This is not
7706
+ protected Blueprint acceptance. The separate \`${name}:verify\` target and
7707
+ protected GitHub workflows retain their committed-history and trust gates.
7708
+
7709
+ Human-supplied secret values are a human-only TTY ceremony. Agents and CI must
7710
+ never invoke \`ebk secret set\` or \`ebk secret remove\`. They may use
7711
+ value-blind metadata, status, verification, and the exact generated-secret
7712
+ initializer, which accepts and returns no plaintext value.
7011
7713
 
7012
7714
  ## Work markers
7013
7715
 
@@ -7067,40 +7769,6 @@ export default tseslint.config(
7067
7769
  rules: { 'no-console': 'error' },
7068
7770
  },
7069
7771
  );
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
7772
  `,
7105
7773
  'docs/architecture.md': `# Workspace architecture
7106
7774
 
@@ -7114,248 +7782,18 @@ The Standard Foundation lives at \`packages/foundation\`. Product slices live be
7114
7782
  \`packages/<slice-name>\` and consume versioned Foundation contracts instead of importing
7115
7783
  Foundation implementation files.
7116
7784
 
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.
7785
+ This workspace's \`${deploymentProfile}\` profile activates only:
7786
+ ${activeStages.join(', ')}. Each active canonical consumer stage uses the matching Foundation.
7787
+ Inactive canonical stages are rejected. Framework-derived personal stages may use only the
7788
+ \`development\` Foundation; generic preview stages are unsupported.
7789
+
7790
+ Every active canonical stage owns a separate Router/domain, Cognito user pool,
7791
+ event bus, secret namespace, IAM boundary, logs, queues, ECR namespace, and
7792
+ stage-qualified SSM discovery contract. Discovery rejects a record for an inactive or different
7793
+ canonical stage. DynamoDB tables belong to individual Slices/tasks, not one global application
7794
+ table. Declared relational features add an independent Aurora cluster per canonical stage,
7795
+ with separate logical databases and migration histories per Slice. Existing relational resources
7796
+ remain declared on upgrade and after removing their last consumer; that is not database retirement.
7359
7797
  `,
7360
7798
  'docs/work-markers.md': `# Agent work markers
7361
7799
 
@@ -7416,7 +7854,7 @@ deploy or refresh before AWS access. Protected remove and unlock remain usable f
7416
7854
  There is no local or silent bypass in this prerelease contract; any future waiver mechanism
7417
7855
  must be committed, reviewable, and auditable.
7418
7856
  `,
7419
- 'ebk.repository-policy.json': json((0, index_js_4.createIncubationRepositoryPolicy)()),
7857
+ 'ebk.repository-policy.json': json(initialRepositoryPolicy),
7420
7858
  'nx.json': json({
7421
7859
  $schema: './node_modules/nx/schemas/nx-schema.json',
7422
7860
  defaultBase: 'develop',
@@ -7456,6 +7894,7 @@ must be committed, reviewable, and auditable.
7456
7894
  inputs: ['default', '^production'],
7457
7895
  },
7458
7896
  },
7897
+ analytics: false,
7459
7898
  neverConnectToCloud: true,
7460
7899
  useDaemonProcess: false,
7461
7900
  }),
@@ -7471,23 +7910,25 @@ must be committed, reviewable, and auditable.
7471
7910
  scripts: {
7472
7911
  build: `nx run-many -t build --exclude=${name}`,
7473
7912
  format: 'nx format:write',
7474
- 'format:check': 'nx format:check',
7913
+ 'format:check': 'nx format:check --all',
7475
7914
  graph: 'nx graph',
7476
7915
  health: `nx run ${name}:workspace-health`,
7477
7916
  lint: `nx run-many -t lint --exclude=${name}`,
7478
7917
  test: `nx run-many -t test --exclude=${name}`,
7479
7918
  typecheck: `nx run-many -t typecheck --exclude=${name}`,
7480
- verify: `nx run ${name}:verify && nx run-many -t verify --exclude=${name}`,
7919
+ verify: `nx format:check --all && nx run ${name}:verify-proposed && nx run-many -t verify --exclude=${name}`,
7481
7920
  },
7482
7921
  dependencies: {
7483
7922
  '@empire-builder-kit/runtime': frameworkVersion,
7484
7923
  },
7485
7924
  devDependencies: {
7486
7925
  '@aws-sdk/client-iam': TOOL_VERSIONS.awsSdk,
7926
+ '@aws-sdk/client-rds': TOOL_VERSIONS.awsSdk,
7487
7927
  '@aws-sdk/client-s3': TOOL_VERSIONS.awsSdk,
7488
7928
  '@aws-sdk/client-secrets-manager': TOOL_VERSIONS.awsSdk,
7489
7929
  '@aws-sdk/client-ssm': TOOL_VERSIONS.awsSdk,
7490
7930
  '@aws-sdk/client-sts': TOOL_VERSIONS.awsSdk,
7931
+ '@aws-sdk/credential-providers': TOOL_VERSIONS.awsSdk,
7491
7932
  '@eslint/js': TOOL_VERSIONS.eslintJs,
7492
7933
  '@empire-builder-kit/nx': frameworkVersion,
7493
7934
  '@types/node': TOOL_VERSIONS.nodeTypes,
@@ -7520,6 +7961,15 @@ must be committed, reviewable, and auditable.
7520
7961
  protectedRef: 'refs/remotes/origin/develop',
7521
7962
  },
7522
7963
  },
7964
+ 'blueprint-trust-check-main': {
7965
+ executor: '@empire-builder-kit/nx:blueprint-trust',
7966
+ options: {
7967
+ operation: 'adopt',
7968
+ output: '.ebk/evidence/supply-chain/blueprint-trust.json',
7969
+ protectedBranch: 'main',
7970
+ protectedRef: 'refs/remotes/origin/main',
7971
+ },
7972
+ },
7523
7973
  'blueprint-trust-check-protected': {
7524
7974
  executor: '@empire-builder-kit/nx:blueprint-trust',
7525
7975
  options: {
@@ -7562,6 +8012,23 @@ must be committed, reviewable, and auditable.
7562
8012
  executor: 'nx:run-commands',
7563
8013
  options: { command: 'node tools/install-ebk-hooks.mjs' },
7564
8014
  },
8015
+ 'stage-authority-render': {
8016
+ cache: false,
8017
+ executor: 'nx:run-commands',
8018
+ outputs: ['{workspaceRoot}/.ebk/authority/{args.stage}/*'],
8019
+ options: {
8020
+ command: 'node tools/stage-authority-render.mjs',
8021
+ forwardAllArgs: true,
8022
+ },
8023
+ },
8024
+ 'deploy-local': {
8025
+ cache: false,
8026
+ dependsOn: ['workspace-health'],
8027
+ executor: '@empire-builder-kit/nx:local-deploy',
8028
+ options: {
8029
+ stage: 'development',
8030
+ },
8031
+ },
7565
8032
  'supply-chain-check': {
7566
8033
  executor: 'nx:run-commands',
7567
8034
  options: {
@@ -7585,64 +8052,27 @@ must be committed, reviewable, and auditable.
7585
8052
  executor: '@empire-builder-kit/nx:work-markers',
7586
8053
  options: {},
7587
8054
  },
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
8055
  verify: {
7602
8056
  dependsOn: ['blueprint-trust-check', 'supply-chain-check'],
7603
8057
  executor: 'nx:run-commands',
7604
8058
  options: { command: 'node tools/workspace-health.mjs' },
7605
8059
  },
8060
+ 'verify-proposed': {
8061
+ dependsOn: ['supply-chain-check'],
8062
+ executor: 'nx:run-commands',
8063
+ options: { command: 'node tools/workspace-health.mjs' },
8064
+ },
7606
8065
  'workspace-health': {
7607
8066
  executor: 'nx:run-commands',
7608
8067
  options: { command: 'node tools/workspace-health.mjs' },
7609
8068
  },
7610
8069
  },
7611
8070
  }),
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
- }),
8071
+ 'packages/foundation/contracts/foundation.json': json(foundationPlan.descriptor),
7643
8072
  'packages/foundation/contracts/costs.json': json({
7644
- schemaVersion: 2,
8073
+ schemaVersion: 3,
7645
8074
  aurora: {
8075
+ declared: foundationPlan.resources.postgres,
7646
8076
  engine: index_js_4.FOUNDATION_AURORA_ENGINE_FAMILY,
7647
8077
  engineVersion: index_js_4.FOUNDATION_AURORA_ENGINE_VERSION,
7648
8078
  },
@@ -7650,19 +8080,29 @@ domain. Without it, the shared Router uses its functional provider URL.
7650
8080
  productionProfileSource: 'ebk.repository-policy.json',
7651
8081
  stages: {
7652
8082
  development: {
7653
- aurora: 'scale-to-zero',
8083
+ aurora: foundationPlan.resources.postgres
8084
+ ? 'scale-to-zero'
8085
+ : 'not-declared',
7654
8086
  dataRetention: 'retained',
7655
- nat: 'ec2',
7656
- },
7657
- staging: {
7658
- aurora: 'scale-to-zero',
7659
- dataRetention: 'retained',
7660
- nat: 'ec2',
8087
+ nat: foundationPlan.resources.network ? 'ec2' : 'none',
7661
8088
  },
8089
+ ...(deploymentProfile === 'starter'
8090
+ ? {}
8091
+ : {
8092
+ staging: {
8093
+ aurora: foundationPlan.resources.postgres
8094
+ ? 'scale-to-zero'
8095
+ : 'not-declared',
8096
+ dataRetention: 'retained',
8097
+ nat: foundationPlan.resources.network ? 'ec2' : 'none',
8098
+ },
8099
+ }),
7662
8100
  production: {
7663
- aurora: 'repository-policy',
8101
+ aurora: foundationPlan.resources.postgres
8102
+ ? 'repository-policy'
8103
+ : 'not-declared',
7664
8104
  dataRetention: 'retained-and-protected',
7665
- nat: 'managed',
8105
+ nat: foundationPlan.resources.network ? 'managed' : 'none',
7666
8106
  },
7667
8107
  },
7668
8108
  usagePriced: [
@@ -7677,68 +8117,51 @@ domain. Without it, the shared Router uses its functional provider URL.
7677
8117
  'packages/foundation/contracts/frontend-security-headers.json': json((0, index_js_2.foundationFrontendSecurityHeaderPolicy)()),
7678
8118
  'packages/foundation/docs/costs.md': `# Foundation cost contract
7679
8119
 
7680
- The VPC NAT path and Aurora capacity are the main fixed-cost decisions.
8120
+ The default Foundation has no Aurora, VPC/NAT, or ECS cluster. Slice-scoped DynamoDB
8121
+ is the first persistence choice. A relational requirement adds Aurora and its
8122
+ network/container migration dependencies; an explicit network workload can also
8123
+ require those dependencies without SQL. Previously provisioned capabilities stay
8124
+ declared until an explicit retirement operation, even with no remaining consumers.
7681
8125
  The machine-readable stage inventory is \`../contracts/costs.json\`; keep this
7682
8126
  seeded decision record aligned with organization-specific budgets and alerts.
7683
8127
 
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.
8128
+ - When network/SQL is declared, development uses EC2 NAT, a development bastion,
8129
+ and Aurora scale-to-zero.
8130
+ ${deploymentProfile === 'starter' ? '' : '- Declared staging network/SQL uses EC2 NAT and Aurora scale-to-zero by default.\n'}- Declared production networking uses managed NAT; production retains protected data. Its explicit Repository
8131
+ Operating Policy profile selects cost-optimized (0–2 ACUs, pause after five idle
8132
+ minutes), balanced (0.5–4 ACUs, always warm), or standard (2–16 ACUs, always warm).
8133
+ Newly generated repositories default to cost-optimized in every deployment profile.
8134
+ Cold resume adds latency; measure it for the workload and validate
8135
+ connection retries, request timeouts, migrations, and background polling.
8136
+ Capacity is shared by all Slices. Open connections can prevent pausing; storage
8137
+ and managed NAT remain billable while Aurora compute is paused.
8138
+ Existing 0.5–4 cost-optimized adopters must explicitly select balanced before
8139
+ upgrade, then opt into cost-optimized separately if cold starts are acceptable.
7689
8140
  - ECS, EventBridge, Cognito, SNS, SSM, and CloudWatch are primarily usage-priced; logs,
7690
8141
  metrics, alarms, and retained data still require active cost review.
7691
8142
 
7692
8143
  Run \`foundation:remove\` only for disposable stages. Production is retained and protected by
7693
8144
  the SST configuration.
7694
- `,
7695
- 'packages/foundation/docs/runbooks.md': `# Foundation runbooks
7696
-
7697
- ## Deployment
7698
-
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
8145
  `,
7730
8146
  'packages/foundation/docs/security.md': `# Foundation security contract
7731
8147
 
7732
8148
  - 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
8149
+ - When relational persistence is declared, the shared Aurora master credential is Foundation-internal.
8150
+ - Declared Aurora IAM authentication is enabled for generated short-lived migration
7735
8151
  tasks. The exact cluster resource ID is non-secret Foundation discovery.
7736
- - Product slices receive scoped logical-database credentials through the provisioning contract.
8152
+ - Relational Product Slices receive scoped logical-database credentials through the provisioning contract.
8153
+ - DynamoDB tables and their access are Slice/task scoped; no shared Foundation application table exists.
7737
8154
  - Cognito owns authentication; product slices own authorization policy and app clients.
7738
8155
  - Lambda and server-side web workloads use resource linking. Containers use the documented
7739
8156
  stage-scoped SSM/secret path and least-privilege IAM.
7740
8157
  - Public domains are configured explicitly through \`EBK_ROOT_DOMAIN\`; generated source never
7741
8158
  contains credentials or customer domain assumptions.
8159
+ - Every stage's CloudFront Router has its own global Web ACL. The baseline always includes AWS
8160
+ Common Rule Set, Known Bad Inputs, Amazon IP Reputation, rate blocking, CloudWatch metrics,
8161
+ and sampled requests. Nonproduction is allowlisted by default. Production is public by
8162
+ default, but cannot deploy until the exact policy has passed a ${rehearsalStage} blocking rehearsal.
8163
+ - Full WAF request logging is disabled by default. Enabling it keeps blocked requests only,
8164
+ uses bounded retention, and redacts query strings, cookies, authorization, and API keys.
7742
8165
 
7743
8166
  The managed \`contracts/frontend-security-headers.json\` file owns the transport
7744
8167
  minimum: HTTPS in deployed environments, one-year HSTS there, and the common
@@ -7770,18 +8193,32 @@ declare const $app: {
7770
8193
 
7771
8194
  import {
7772
8195
  FOUNDATION_SSM_SCHEMA_VERSION,
7773
- STANDARD_FOUNDATION_CAPABILITIES,
8196
+ createRouterWafRules,
8197
+ createRouterWafStagePlan,
8198
+ ${foundationPlan.resources.postgres ? ' foundationDatabaseEndpointHost,' : ''}
7774
8199
  foundationSsmPath,
8200
+ normalizeRouterWafPolicy,
8201
+ reconcileRouterWafBlockingRehearsal,
8202
+ routerWafContractDigest,
7775
8203
  type FoundationSsmKey,
7776
8204
  } from '@empire-builder-kit/nx/foundation';
7777
8205
  import {
7778
- FOUNDATION_AURORA_ENGINE,
7779
- FOUNDATION_AURORA_ENGINE_VERSION,
8206
+ ${foundationPlan.resources.postgres ? ' FOUNDATION_AURORA_ENGINE,\n FOUNDATION_AURORA_ENGINE_VERSION,' : ''}
7780
8207
  productionFoundationProfileContract,
7781
8208
  } from '@empire-builder-kit/nx/repository-policy';
8209
+ import { installStageIamBoundaryTransforms } from '@empire-builder-kit/nx/aws-authority';
8210
+ import { createStageAuthorityIdentity } from '@empire-builder-kit/nx/aws-authority';
8211
+ import {
8212
+ configuredDeploymentAccountForStage,
8213
+ configuredDeploymentRegion,
8214
+ productionRehearsalStage,
8215
+ } from '@empire-builder-kit/nx/repository-policy';
7782
8216
  import { createFoundationAccountCostControlPlan } from '@empire-builder-kit/nx/performance';
7783
8217
  import accountCostPolicy from '../performance/account-cost-policy.json';
7784
- import type { FoundationWorkloadRolePreflight } from './workload-roles/preflight.js';
8218
+ import routerWafBlockingRehearsal from '../security/router-waf-blocking-rehearsal.json';
8219
+ import routerWafLiveEvidence from '../security/router-waf-live-evidence.json';
8220
+ import routerWafPolicy from '../security/router-waf.json';
8221
+ import repositoryPolicy from '../../../ebk.repository-policy.json';
7785
8222
 
7786
8223
  function identityDomainPrefix(stage: string, accountId: string): string {
7787
8224
  if (!/^\\d{12}$/.test(accountId)) {
@@ -7802,7 +8239,7 @@ function identityDomainPrefix(stage: string, accountId: string): string {
7802
8239
  return value.slice(0, 50) + '-' + digest;
7803
8240
  }
7804
8241
 
7805
- function publishFoundation(values: Record<FoundationSsmKey, any>) {
8242
+ function publishFoundation(values: Partial<Record<FoundationSsmKey, any>>) {
7806
8243
  return Object.fromEntries(
7807
8244
  Object.entries(values).map(([key, value]) => [
7808
8245
  key,
@@ -7815,45 +8252,78 @@ function publishFoundation(values: Record<FoundationSsmKey, any>) {
7815
8252
  );
7816
8253
  }
7817
8254
 
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
- }
8255
+ export function foundation() {
7826
8256
  const isProduction = $app.stage === 'production';
7827
- const isStaging = $app.stage === 'staging';
8257
+ ${foundationPlan.resources.postgres ? " const isStaging = $app.stage === 'staging';" : ''}
7828
8258
  const rootDomain = process.env.EBK_ROOT_DOMAIN?.trim();
7829
8259
  const productionConfiguration = isProduction
7830
8260
  ? productionFoundationProfileContract(
7831
8261
  process.env.EBK_PRODUCTION_FOUNDATION_PROFILE,
7832
8262
  )
7833
8263
  : 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
- },
8264
+ ${foundationPlan.resources.network ? " const natMode = productionConfiguration?.contract.nat ?? 'ec2';" : ''}
8265
+ const normalizedRouterWafPolicy = normalizeRouterWafPolicy(routerWafPolicy);
8266
+ if (repositoryPolicy.deploymentProfile !== normalizedRouterWafPolicy.deploymentProfile) {
8267
+ throw new Error('Router WAF policy profile differs from committed repository policy.');
8268
+ }
8269
+ const rehearsalStage = productionRehearsalStage(normalizedRouterWafPolicy.deploymentProfile);
8270
+ const rehearsalAccount = configuredDeploymentAccountForStage(
8271
+ normalizedRouterWafPolicy.deploymentProfile,
8272
+ repositoryPolicy.deploymentAccounts,
8273
+ rehearsalStage,
8274
+ );
8275
+ const rehearsalRegion = configuredDeploymentRegion(repositoryPolicy.deploymentRegion);
8276
+ const rehearsalRoleArn = createStageAuthorityIdentity({
8277
+ accountId: rehearsalAccount,
8278
+ partition: rehearsalRegion.startsWith('us-gov-') ? 'aws-us-gov' : 'aws',
8279
+ region: rehearsalRegion,
8280
+ stage: rehearsalStage,
8281
+ }).deploymentRoleArn;
8282
+ const wafPlan = createRouterWafStagePlan({
8283
+ policy: normalizedRouterWafPolicy,
8284
+ rehearsal: isProduction
8285
+ ? reconcileRouterWafBlockingRehearsal({
8286
+ evidence: routerWafLiveEvidence,
8287
+ expected: {
8288
+ accountId: rehearsalAccount,
8289
+ deploymentRoleArn: rehearsalRoleArn,
8290
+ region: rehearsalRegion,
8291
+ stage: rehearsalStage,
8292
+ wafContractDigest: routerWafContractDigest(normalizedRouterWafPolicy),
8293
+ workspace: '${name}',
8294
+ },
8295
+ policy: normalizedRouterWafPolicy,
8296
+ receipt: routerWafBlockingRehearsal,
8297
+ })
8298
+ : null,
8299
+ stage: $app.stage,
8300
+ });
8301
+ const deploymentAccount = process.env.AWS_EXPECTED_ACCOUNT_ID?.trim() ?? '';
8302
+ if (!/^\\d{12}$/.test(deploymentAccount)) {
8303
+ throw new Error(
8304
+ 'Foundation deployment requires the exact committed AWS account before infrastructure construction.',
7845
8305
  );
7846
8306
  }
8307
+ installStageIamBoundaryTransforms({
8308
+ accountId: deploymentAccount,
8309
+ instanceProfileResource: aws.iam.InstanceProfile,
8310
+ registerTransform: $transform,
8311
+ roleResource: aws.iam.Role,
8312
+ stage: ($dev ? 'development' : $app.stage) as
8313
+ | 'development'
8314
+ | 'staging'
8315
+ | 'production',
8316
+ });
7847
8317
 
7848
- const vpc = new sst.aws.Vpc('FoundationVpc', {
8318
+ ${foundationPlan.resources.network
8319
+ ? ` const vpc = new sst.aws.Vpc('FoundationVpc', {
7849
8320
  az: 2,
7850
8321
  bastion: $app.stage === 'development',
7851
8322
  nat:
7852
- natMode === 'ec2'
7853
- ? (natBinding?.vpc.nat ?? 'ec2')
7854
- : 'managed',
7855
- });
7856
- const cluster = new sst.aws.Cluster('FoundationCluster', { vpc });
8323
+ natMode === 'ec2' ? 'ec2' : 'managed',
8324
+ });`
8325
+ : ''}
8326
+ ${foundationPlan.resources.containerCompute ? " const cluster = new sst.aws.Cluster('FoundationCluster', { vpc });" : ''}
7857
8327
  const router = new sst.aws.Router('FoundationRouter', {
7858
8328
  ...(rootDomain
7859
8329
  ? {
@@ -7862,10 +8332,138 @@ export function foundation(
7862
8332
  aliases: ['*.' + rootDomain],
7863
8333
  dns: sst.aws.dns(),
7864
8334
  },
7865
- }
8335
+ }
7866
8336
  : {}),
8337
+ waf: {
8338
+ logging: wafPlan.logging,
8339
+ managedRules: {
8340
+ coreRuleSet: true,
8341
+ knownBadInputs: true,
8342
+ sqlInjection: false,
8343
+ },
8344
+ rateLimitPerIp: wafPlan.rateLimitPerIp,
8345
+ },
8346
+ transform: {
8347
+ cdn(args: any) {
8348
+ if (!args.webAclArn) {
8349
+ throw new Error(
8350
+ 'Foundation Router must attach its exact global Web ACL.',
8351
+ );
8352
+ }
8353
+ args.tags = {
8354
+ ...(args.tags ?? {}),
8355
+ 'ebk:managed-by': 'empire-builder-kit',
8356
+ 'ebk:owner': 'foundation',
8357
+ 'ebk:stage': wafPlan.stage,
8358
+ 'ebk:workspace': '${name}',
8359
+ };
8360
+ },
8361
+ waf(args: any, opts: any) {
8362
+ if (args.scope !== 'CLOUDFRONT') {
8363
+ throw new Error(
8364
+ 'Foundation Router Web ACL must use global CloudFront scope.',
8365
+ );
8366
+ }
8367
+ const ipSets: { ipv4Arn?: any; ipv6Arn?: any; smokeArn?: any } = {};
8368
+ if (wafPlan.ipv4Cidrs.length > 0) {
8369
+ ipSets.ipv4Arn = new aws.wafv2.IpSet(
8370
+ 'FoundationRouterIpv4Allowlist',
8371
+ {
8372
+ addresses: wafPlan.ipv4Cidrs,
8373
+ ipAddressVersion: 'IPV4',
8374
+ name: 'foundation-' + wafPlan.stage + '-router-ipv4-allowlist',
8375
+ scope: 'CLOUDFRONT',
8376
+ tags: {
8377
+ 'ebk:managed-by': 'empire-builder-kit',
8378
+ 'ebk:owner': 'foundation',
8379
+ 'ebk:policy-digest': wafPlan.policyDigest,
8380
+ 'ebk:stage': wafPlan.stage,
8381
+ 'ebk:workspace': '${name}',
8382
+ },
8383
+ },
8384
+ { provider: opts.provider },
8385
+ ).arn;
8386
+ }
8387
+ if (wafPlan.ipv6Cidrs.length > 0) {
8388
+ ipSets.ipv6Arn = new aws.wafv2.IpSet(
8389
+ 'FoundationRouterIpv6Allowlist',
8390
+ {
8391
+ addresses: wafPlan.ipv6Cidrs,
8392
+ ipAddressVersion: 'IPV6',
8393
+ name: 'foundation-' + wafPlan.stage + '-router-ipv6-allowlist',
8394
+ scope: 'CLOUDFRONT',
8395
+ tags: {
8396
+ 'ebk:managed-by': 'empire-builder-kit',
8397
+ 'ebk:owner': 'foundation',
8398
+ 'ebk:policy-digest': wafPlan.policyDigest,
8399
+ 'ebk:stage': wafPlan.stage,
8400
+ 'ebk:workspace': '${name}',
8401
+ },
8402
+ },
8403
+ { provider: opts.provider },
8404
+ ).arn;
8405
+ }
8406
+ if (wafPlan.access === 'allowlist') {
8407
+ // Smoke rotates WAF's LockToken outside Pulumi. Keep the existing
8408
+ // baseline IP sets untouched; AWS's Cloud Control handler owns the
8409
+ // separate ephemeral set's lifecycle without a cached lock token.
8410
+ const smokeAccess = new aws.cloudcontrol.Resource(
8411
+ 'FoundationRouterSmokeAccess',
8412
+ {
8413
+ typeName: 'AWS::WAFv2::IPSet',
8414
+ desiredState: JSON.stringify({
8415
+ Addresses: [],
8416
+ IPAddressVersion: 'IPV4',
8417
+ Name: 'foundation-' + wafPlan.stage + '-router-smoke-access',
8418
+ Scope: 'CLOUDFRONT',
8419
+ Tags: Object.entries({
8420
+ 'ebk:managed-by': 'empire-builder-kit',
8421
+ 'ebk:owner': 'foundation',
8422
+ 'ebk:purpose': 'private-smoke-access',
8423
+ 'ebk:stage': wafPlan.stage,
8424
+ 'ebk:workspace': '${name}',
8425
+ }).map(([Key, Value]) => ({ Key, Value })),
8426
+ }),
8427
+ },
8428
+ {
8429
+ provider: opts.provider,
8430
+ // This dedicated set has creation-only identity/ownership inputs.
8431
+ // Admission owns its addresses; the Web ACL owns policy binding.
8432
+ // Retain old inputs on upgrade too: Cloud Control patches arrays
8433
+ // by index, which is unsafe for service-reordered Tags (including
8434
+ // removing an older policy-digest tag). Never patch this document.
8435
+ ignoreChanges: ['desiredState'],
8436
+ },
8437
+ );
8438
+ ipSets.smokeArn = smokeAccess.properties.apply(
8439
+ (properties: string) => JSON.parse(properties).Arn,
8440
+ );
8441
+ }
8442
+ args.defaultAction = { allow: {} };
8443
+ args.rules = createRouterWafRules(wafPlan, ipSets);
8444
+ args.scope = 'CLOUDFRONT';
8445
+ args.tags = {
8446
+ ...(args.tags ?? {}),
8447
+ 'ebk:managed-by': 'empire-builder-kit',
8448
+ 'ebk:owner': 'foundation',
8449
+ 'ebk:policy-digest': wafPlan.policyDigest,
8450
+ 'ebk:stage': wafPlan.stage,
8451
+ 'ebk:workspace': '${name}',
8452
+ };
8453
+ args.visibilityConfig = {
8454
+ cloudwatchMetricsEnabled: true,
8455
+ metricName:
8456
+ 'Ebk' +
8457
+ wafPlan.stage[0].toUpperCase() +
8458
+ wafPlan.stage.slice(1) +
8459
+ 'FoundationRouterWaf',
8460
+ sampledRequestsEnabled: true,
8461
+ };
8462
+ },
8463
+ },
7867
8464
  });
7868
- const database = new sst.aws.Aurora('FoundationDatabase', {
8465
+ ${foundationPlan.resources.postgres
8466
+ ? ` const database = new sst.aws.Aurora('FoundationDatabase', {
7869
8467
  // SQL bootstrap and migrations run inside the VPC. Keep the public HTTPS
7870
8468
  // Data API surface disabled; protected GitHub roles also deny its actions.
7871
8469
  dataApi: false,
@@ -7887,6 +8485,47 @@ export function foundation(
7887
8485
  },
7888
8486
  vpc,
7889
8487
  });
8488
+ const databaseClusterEndpoint = database.nodes.cluster.endpoint.apply(
8489
+ foundationDatabaseEndpointHost,
8490
+ );
8491
+ const databaseReaderEndpoint = database.nodes.cluster.readerEndpoint.apply(
8492
+ foundationDatabaseEndpointHost,
8493
+ );
8494
+ // SST v4.17.1 creates a top-level LinkRef on the next tick and its Aurora
8495
+ // implementation unconditionally splits cluster endpoints. During a
8496
+ // governed refresh after an interrupted first deploy, those provider
8497
+ // outputs are legitimately absent. Preserve the normal link contract while
8498
+ // keeping that read-only recovery program total; a real deploy resolves the
8499
+ // exact endpoints before any discovery resource is created.
8500
+ database.getSSTLink = () => ({
8501
+ include: [
8502
+ sst.aws.permission({
8503
+ actions: ['secretsmanager:GetSecretValue'],
8504
+ resources: [database.secretArn],
8505
+ }),
8506
+ sst.aws.permission({
8507
+ actions: [
8508
+ 'rds-data:BatchExecuteStatement',
8509
+ 'rds-data:BeginTransaction',
8510
+ 'rds-data:CommitTransaction',
8511
+ 'rds-data:ExecuteStatement',
8512
+ 'rds-data:RollbackTransaction',
8513
+ ],
8514
+ resources: [database.clusterArn],
8515
+ }),
8516
+ ],
8517
+ properties: {
8518
+ clusterArn: database.clusterArn,
8519
+ database: database.database,
8520
+ host: databaseClusterEndpoint,
8521
+ password: database.password,
8522
+ port: database.port,
8523
+ reader: databaseReaderEndpoint,
8524
+ secretArn: database.secretArn,
8525
+ username: database.username,
8526
+ },
8527
+ });`
8528
+ : ''}
7890
8529
  const foundationAccountId = aws.getCallerIdentityOutput({}).accountId;
7891
8530
  const foundationPartition = aws.getPartitionOutput({});
7892
8531
  const foundationRegion = aws.getRegionOutput({});
@@ -7915,11 +8554,12 @@ export function foundation(
7915
8554
  const accountCostControl = createFoundationAccountCostControlPlan({
7916
8555
  evaluatedAt: new Date().toISOString(),
7917
8556
  policy: accountCostPolicy,
8557
+ rehearsalStage: '${rehearsalStage}',
7918
8558
  region: process.env.AWS_REGION,
7919
8559
  stage: $app.stage,
7920
8560
  workspace: '${name}',
7921
8561
  });
7922
- const databaseInstanceId = database.nodes.instance.identifier;
8562
+ ${foundationPlan.resources.postgres ? ' const databaseInstanceId = database.nodes.instance.identifier;' : ''}
7923
8563
  const alertTopicPolicy = new aws.sns.TopicPolicy(
7924
8564
  'FoundationAlertTopicPolicy',
7925
8565
  {
@@ -7949,6 +8589,7 @@ export function foundation(
7949
8589
  },
7950
8590
  ],
7951
8591
  resources: [alertTopic.arn],
8592
+ sid: 'AllowBudgetAlerts',
7952
8593
  },
7953
8594
  {
7954
8595
  actions: ['sns:Publish'],
@@ -7962,8 +8603,10 @@ export function foundation(
7962
8603
  type: 'Service',
7963
8604
  }],
7964
8605
  resources: [alertTopic.arn],
8606
+ sid: 'AllowCostAnomalyAlerts',
7965
8607
  },
7966
- {
8608
+ ${foundationPlan.resources.postgres
8609
+ ? ` {
7967
8610
  actions: ['sns:Publish'],
7968
8611
  conditions: [
7969
8612
  {
@@ -7984,7 +8627,9 @@ export function foundation(
7984
8627
  type: 'Service',
7985
8628
  }],
7986
8629
  resources: [alertTopic.arn],
7987
- },
8630
+ sid: 'AllowRdsEventAlerts',
8631
+ },`
8632
+ : ''}
7988
8633
  {
7989
8634
  actions: ['sns:Publish'],
7990
8635
  conditions: [
@@ -8008,6 +8653,7 @@ export function foundation(
8008
8653
  },
8009
8654
  ],
8010
8655
  resources: [alertTopic.arn],
8656
+ sid: 'AllowCloudWatchAlarmAlerts',
8011
8657
  },
8012
8658
  ],
8013
8659
  }).json,
@@ -8036,7 +8682,43 @@ export function foundation(
8036
8682
  { dependsOn: [alertTopicPolicy] },
8037
8683
  )
8038
8684
  : undefined;
8039
- const databaseEvents = isProduction
8685
+ const accountCostAnomalyMonitor = isProduction
8686
+ ? new aws.costexplorer.AnomalyMonitor(
8687
+ 'FoundationAccountCostAnomalyMonitor',
8688
+ {
8689
+ monitorDimension: 'SERVICE',
8690
+ monitorType: 'DIMENSIONAL',
8691
+ name: '${name}-account-service-cost-anomalies',
8692
+ tags: {
8693
+ 'ebk:cost-policy-digest': accountCostControl.policyDigest,
8694
+ 'ebk:cost-scope': '${name}/account/production',
8695
+ 'ebk:owner': accountCostControl.owner,
8696
+ },
8697
+ },
8698
+ )
8699
+ : undefined;
8700
+ const accountCostAnomalySubscription =
8701
+ accountCostControl.anomaly && accountCostAnomalyMonitor
8702
+ ? new aws.costexplorer.AnomalySubscription(
8703
+ 'FoundationAccountCostAnomalySubscription',
8704
+ {
8705
+ frequency: accountCostControl.anomaly.subscription.frequency,
8706
+ monitorArnLists: [accountCostAnomalyMonitor.arn],
8707
+ name: accountCostControl.anomaly.subscription.name,
8708
+ subscribers: [{ address: alertTopic.arn, type: 'SNS' }],
8709
+ tags: {
8710
+ 'ebk:cost-policy-digest': accountCostControl.policyDigest,
8711
+ 'ebk:cost-scope': '${name}/account/production',
8712
+ 'ebk:owner': accountCostControl.owner,
8713
+ },
8714
+ thresholdExpression:
8715
+ accountCostControl.anomaly.subscription.thresholdExpression,
8716
+ },
8717
+ { dependsOn: [accountCostAnomalyMonitor, alertTopicPolicy] },
8718
+ )
8719
+ : undefined;
8720
+ ${foundationPlan.resources.postgres
8721
+ ? ` const databaseEvents = isProduction
8040
8722
  ? new aws.rds.EventSubscription(
8041
8723
  'FoundationDatabaseEvents',
8042
8724
  {
@@ -8118,7 +8800,14 @@ export function foundation(
8118
8800
  threshold: 90,
8119
8801
  treatMissingData: 'notBreaching',
8120
8802
  },
8121
- );
8803
+ );`
8804
+ : ` const dashboard = new aws.cloudwatch.Dashboard('FoundationDashboard', {
8805
+ dashboardName: 'ebk-' + $app.stage + '-foundation',
8806
+ dashboardBody: JSON.stringify({ widgets: [{
8807
+ type: 'text', x: 0, y: 0, width: 24, height: 3,
8808
+ properties: { markdown: '# Empire Builder Kit Foundation\\nStage: ' + $app.stage + '\\nPersistence: Slice-scoped DynamoDB; SQL not declared.' },
8809
+ }] }),
8810
+ });`}
8122
8811
 
8123
8812
  if (new Set(['development', 'staging', 'production']).has($app.stage)) {
8124
8813
  new aws.cloudwatch.EventArchive('FoundationEventArchive', {
@@ -8134,18 +8823,18 @@ export function foundation(
8134
8823
  accountCostControl.authority.monthlyMaximumUsd,
8135
8824
  ),
8136
8825
  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
- ),
8826
+ capabilities: ${JSON.stringify(foundationPlan.descriptor.capabilities.join(','))},
8827
+ ${foundationPlan.resources.containerCompute ? ' clusterId: cluster.id,' : ''}
8828
+ contractVersion: '${foundationPlan.descriptor.contractVersion}',
8829
+ ${foundationPlan.resources.postgres
8830
+ ? ` databaseClusterArn: database.clusterArn,
8831
+ databaseClusterEndpoint,
8144
8832
  databaseClusterId: database.id,
8145
8833
  databaseClusterResourceId: database.nodes.cluster.clusterResourceId,
8146
- databaseHost: database.host,
8834
+ databaseHost: databaseClusterEndpoint,
8147
8835
  databaseMasterSecretArn: database.secretArn,
8148
- databasePort: database.port.apply(String),
8836
+ databasePort: database.port.apply(String),`
8837
+ : ''}
8149
8838
  eventBusArn: eventBus.arn,
8150
8839
  eventBusName: eventBus.name,
8151
8840
  hasPublicDomain: rootDomain ? 'true' : 'false',
@@ -8156,19 +8845,22 @@ export function foundation(
8156
8845
  routerDistributionId: router.distributionID,
8157
8846
  routerUrl: router.url,
8158
8847
  schemaVersion: String(FOUNDATION_SSM_SCHEMA_VERSION),
8159
- vpcId: vpc.id,
8848
+ ${foundationPlan.resources.network ? ' vpcId: vpc.id,' : ''}
8160
8849
  });
8161
8850
 
8162
8851
  return {
8163
8852
  accountBudgetName: accountBudget?.name,
8164
8853
  alertTopicArn: alertTopic.arn,
8165
- capabilities: STANDARD_FOUNDATION_CAPABILITIES,
8166
- clusterId: cluster.id,
8167
- contractVersion: '1.0.0',
8168
- databaseCapacityAlarmName: databaseCapacityAlarm.name,
8854
+ capabilities: ${JSON.stringify(foundationPlan.descriptor.capabilities)},
8855
+ ${foundationPlan.resources.containerCompute ? ' clusterId: cluster.id,' : ''}
8856
+ contractVersion: '${foundationPlan.descriptor.contractVersion}',
8857
+ costAnomalySubscriptionArn: accountCostAnomalySubscription?.arn,
8858
+ ${foundationPlan.resources.postgres ? ' databaseCapacityAlarmName: databaseCapacityAlarm.name,' : ''}
8169
8859
  dashboardName: dashboard.dashboardName,
8170
- databaseClusterId: database.id,
8171
- databaseEventSubscriptionId: databaseEvents?.id,
8860
+ ${foundationPlan.resources.postgres
8861
+ ? ` databaseClusterId: database.id,
8862
+ databaseEventSubscriptionId: databaseEvents?.id,`
8863
+ : ''}
8172
8864
  eventBusName: eventBus.name,
8173
8865
  identityUserPoolId: userPool.id,
8174
8866
  identityTokenEndpoint,
@@ -8176,33 +8868,68 @@ export function foundation(
8176
8868
  productionFoundationProfile: productionConfiguration?.profile,
8177
8869
  rootDomain,
8178
8870
  routerUrl: router.url,
8179
- vpcId: vpc.id,
8871
+ ${foundationPlan.resources.network ? ' vpcId: vpc.id,' : ''}
8180
8872
  };
8181
8873
  }
8182
8874
  `,
8183
8875
  'packages/foundation/infra/index.ts': `import { foundation } from './foundation.js';
8184
- import type { FoundationWorkloadRolePreflight } from './workload-roles/preflight.js';
8185
8876
 
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
- };
8877
+ export function app() {
8878
+ return foundation();
8193
8879
  }
8194
8880
  `,
8195
8881
  'packages/foundation/performance/account-cost-policy.json': json({
8196
8882
  actualNotificationThresholdPercent: 80,
8197
8883
  forecastNotificationThresholdPercent: 100,
8198
8884
  kind: 'ebk-foundation-account-cost-policy',
8199
- monthlyMaximumUsd: 1_000,
8885
+ monthlyMaximumUsd: deploymentProfile === 'starter' ? 250 : 1_000,
8200
8886
  owner: 'replace-with-owner',
8201
8887
  reviewAt: null,
8202
8888
  schemaVersion: 1,
8203
8889
  status: 'example',
8204
8890
  workspace: name,
8205
8891
  }),
8892
+ 'packages/foundation/security/router-waf.json': json({
8893
+ deploymentProfile,
8894
+ kind: 'ebk-router-waf-policy',
8895
+ logging: {
8896
+ enabled: false,
8897
+ retention: '1 week',
8898
+ },
8899
+ rateLimitPerIp: 2_000,
8900
+ schemaVersion: 1,
8901
+ stages: {
8902
+ development: {
8903
+ access: 'allowlist',
8904
+ allowedCidrs: [],
8905
+ mode: 'block',
8906
+ },
8907
+ production: {
8908
+ access: 'public',
8909
+ allowedCidrs: [],
8910
+ mode: 'block',
8911
+ },
8912
+ ...(deploymentProfile === 'starter'
8913
+ ? {}
8914
+ : {
8915
+ staging: {
8916
+ access: 'allowlist',
8917
+ allowedCidrs: [],
8918
+ mode: 'block',
8919
+ },
8920
+ }),
8921
+ },
8922
+ }),
8923
+ 'packages/foundation/security/router-waf-blocking-rehearsal.json': json({
8924
+ kind: 'ebk-router-waf-blocking-rehearsal',
8925
+ schemaVersion: 4,
8926
+ status: 'pending',
8927
+ }),
8928
+ 'packages/foundation/security/router-waf-live-evidence.json': json({
8929
+ kind: 'ebk-router-waf-live-evidence',
8930
+ schemaVersion: 4,
8931
+ status: 'pending',
8932
+ }),
8206
8933
  'packages/foundation/configuration/declarations.json': json({
8207
8934
  project: 'foundation',
8208
8935
  schemaVersion: 1,
@@ -8235,29 +8962,87 @@ export function app(workloadRoles?: FoundationWorkloadRolePreflight) {
8235
8962
  version: '4.17.1',
8236
8963
  },
8237
8964
  policy: {
8965
+ adoptedImports: [],
8238
8966
  additiveLowRiskTypes: [
8239
8967
  'aws:cloudwatch/dashboard:Dashboard',
8240
8968
  'aws:cloudwatch/logGroup:LogGroup',
8241
8969
  'aws:cloudwatch/metricAlarm:MetricAlarm',
8242
8970
  ],
8971
+ additiveReviewedTypes: [
8972
+ 'aws:cloudwatch/eventArchive:EventArchive',
8973
+ 'aws:cloudwatch/eventBus:EventBus',
8974
+ 'aws:cognito/userPoolDomain:UserPoolDomain',
8975
+ 'aws:ec2/defaultSecurityGroup:DefaultSecurityGroup',
8976
+ 'aws:ec2/internetGateway:InternetGateway',
8977
+ 'aws:ec2/routeTable:RouteTable',
8978
+ 'aws:ec2/routeTableAssociation:RouteTableAssociation',
8979
+ 'aws:ec2/securityGroup:SecurityGroup',
8980
+ 'aws:ec2/subnet:Subnet',
8981
+ 'aws:ec2/vpc:Vpc',
8982
+ 'aws:ecs/cluster:Cluster',
8983
+ 'aws:ecs/clusterCapacityProviders:ClusterCapacityProviders',
8984
+ 'aws:rds/clusterParameterGroup:ClusterParameterGroup',
8985
+ 'aws:rds/eventSubscription:EventSubscription',
8986
+ 'aws:rds/parameterGroup:ParameterGroup',
8987
+ 'aws:rds/subnetGroup:SubnetGroup',
8988
+ 'aws:servicediscovery/privateDnsNamespace:PrivateDnsNamespace',
8989
+ 'aws:sns/topic:Topic',
8990
+ 'aws:sns/topicPolicy:TopicPolicy',
8991
+ 'aws:wafv2/webAclLoggingConfiguration:WebAclLoggingConfiguration',
8992
+ 'pulumi-nodejs:dynamic:Resource',
8993
+ 'pulumi:providers:aws',
8994
+ 'pulumi:providers:pulumi-nodejs',
8995
+ 'pulumi:providers:random',
8996
+ 'pulumi:pulumi:Stack',
8997
+ 'sst:aws:Aurora',
8998
+ 'sst:aws:Bus',
8999
+ 'sst:aws:CDN',
9000
+ 'sst:aws:Cluster',
9001
+ 'sst:aws:CognitoUserPool',
9002
+ 'sst:aws:Router',
9003
+ 'sst:aws:SnsTopic',
9004
+ 'sst:aws:Vpc',
9005
+ 'sst:sst:LinkRef',
9006
+ 'sst:sst:Version',
9007
+ ],
8243
9008
  costOrQuotaTypes: [
8244
9009
  'aws:apigatewayv2/api:Api',
8245
9010
  'aws:budgets/budget:Budget',
9011
+ 'aws:cloudfront/cachePolicy:CachePolicy',
8246
9012
  'aws:cloudfront/distribution:Distribution',
9013
+ 'aws:cloudfront/function:Function',
9014
+ 'aws:cloudfront/keyValueStore:KeyValueStore',
9015
+ 'aws:costexplorer/anomalyMonitor:AnomalyMonitor',
9016
+ 'aws:costexplorer/anomalySubscription:AnomalySubscription',
8247
9017
  'aws:cognito/userPool:UserPool',
9018
+ 'aws:ec2/eip:Eip',
9019
+ 'aws:ec2/eipAssociation:EipAssociation',
9020
+ 'aws:ec2/instance:Instance',
8248
9021
  'aws:ec2/natGateway:NatGateway',
8249
9022
  'aws:rds/cluster:Cluster',
9023
+ 'aws:rds/clusterInstance:ClusterInstance',
9024
+ 'aws:wafv2/ipSet:IpSet',
9025
+ 'aws:wafv2/webAcl:WebAcl',
8250
9026
  ],
8251
9027
  failClosedOnUnknown: true,
8252
9028
  protectedPlanAuthority: 'deploy-role-residual',
8253
9029
  standingPolicyRiskClasses: ['no-change', 'additive-low-risk'],
8254
9030
  statefulTypes: [
9031
+ 'aws:cloudwatch/eventArchive:EventArchive',
9032
+ 'aws:cloudwatch/eventBus:EventBus',
8255
9033
  'aws:cognito/userPool:UserPool',
8256
9034
  'aws:dynamodb/table:Table',
8257
9035
  'aws:events/eventBus:EventBus',
8258
9036
  'aws:kms/key:Key',
8259
9037
  'aws:rds/cluster:Cluster',
8260
9038
  'aws:s3/bucket:Bucket',
9039
+ 'aws:secretsmanager/secret:Secret',
9040
+ 'aws:secretsmanager/secretVersion:SecretVersion',
9041
+ 'aws:sns/topic:Topic',
9042
+ 'aws:ssm/parameter:Parameter',
9043
+ 'aws:wafv2/ipSet:IpSet',
9044
+ 'aws:wafv2/webAcl:WebAcl',
9045
+ 'random:index/randomPassword:RandomPassword',
8261
9046
  ],
8262
9047
  },
8263
9048
  project: 'foundation',
@@ -8287,10 +9072,6 @@ export function app(workloadRoles?: FoundationWorkloadRolePreflight) {
8287
9072
  'verify',
8288
9073
  { target: 'profile-preflight', params: 'forward' },
8289
9074
  ],
8290
- outputs: [
8291
- `{projectRoot}/.ebk/evidence/infrastructure-convergence.json`,
8292
- `{projectRoot}/.ebk/evidence/infrastructure-mutation-binding.json`,
8293
- ],
8294
9075
  options: {
8295
9076
  cwd: 'packages/foundation',
8296
9077
  stage: 'development',
@@ -8307,15 +9088,7 @@ export function app(workloadRoles?: FoundationWorkloadRolePreflight) {
8307
9088
  'infrastructure-plan': {
8308
9089
  cache: false,
8309
9090
  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
9091
  options: {
8318
- action: 'deploy',
8319
9092
  cwd: 'packages/foundation',
8320
9093
  stage: 'development',
8321
9094
  stageMode: 'foundation',
@@ -8324,10 +9097,6 @@ export function app(workloadRoles?: FoundationWorkloadRolePreflight) {
8324
9097
  refresh: {
8325
9098
  executor: '@empire-builder-kit/nx:refresh',
8326
9099
  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
9100
  options: {
8332
9101
  cwd: 'packages/foundation',
8333
9102
  stage: 'development',
@@ -8337,10 +9106,6 @@ export function app(workloadRoles?: FoundationWorkloadRolePreflight) {
8337
9106
  remove: {
8338
9107
  executor: '@empire-builder-kit/nx:remove',
8339
9108
  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
9109
  options: {
8345
9110
  cwd: 'packages/foundation',
8346
9111
  stage: 'development',
@@ -8355,6 +9120,13 @@ export function app(workloadRoles?: FoundationWorkloadRolePreflight) {
8355
9120
  executor: '@empire-builder-kit/nx:foundation-profile-preflight',
8356
9121
  options: { stage: 'development' },
8357
9122
  },
9123
+ 'router-waf-rehearsal': {
9124
+ cache: false,
9125
+ executor: '@empire-builder-kit/nx:router-waf-rehearsal',
9126
+ outputs: [
9127
+ `{projectRoot}/security/router-waf-blocking-rehearsal.json`,
9128
+ ],
9129
+ },
8358
9130
  typecheck: {
8359
9131
  executor: 'nx:run-commands',
8360
9132
  options: {
@@ -8382,6 +9154,19 @@ export function app(workloadRoles?: FoundationWorkloadRolePreflight) {
8382
9154
  }),
8383
9155
  'packages/foundation/scripts/verify-foundation.mjs': `import { readFileSync } from 'node:fs';
8384
9156
 
9157
+ import {
9158
+ normalizeRouterWafBlockingRehearsal,
9159
+ normalizeRouterWafPolicy,
9160
+ reconcileRouterWafBlockingRehearsal,
9161
+ routerWafContractDigest,
9162
+ } from '@empire-builder-kit/nx/foundation';
9163
+ import { createStageAuthorityIdentity } from '@empire-builder-kit/nx/aws-authority';
9164
+ import {
9165
+ configuredDeploymentAccountForStage,
9166
+ configuredDeploymentRegion,
9167
+ productionRehearsalStage,
9168
+ } from '@empire-builder-kit/nx/repository-policy';
9169
+
8385
9170
  const descriptor = JSON.parse(
8386
9171
  readFileSync(new URL('../contracts/foundation.json', import.meta.url), 'utf8'),
8387
9172
  );
@@ -8397,35 +9182,92 @@ const frontendSecurityHeaders = JSON.parse(
8397
9182
  'utf8',
8398
9183
  ),
8399
9184
  );
8400
- const expectedCapabilities = [
8401
- 'network',
8402
- 'routing',
8403
- 'container-compute',
8404
- 'postgres',
8405
- 'identity',
8406
- 'events',
8407
- 'observability',
8408
- 'configuration',
8409
- 'deployment',
8410
- ];
9185
+ const routerWafPolicy = normalizeRouterWafPolicy(
9186
+ JSON.parse(
9187
+ readFileSync(
9188
+ new URL('../security/router-waf.json', import.meta.url),
9189
+ 'utf8',
9190
+ ),
9191
+ ),
9192
+ );
9193
+ const repositoryPolicy = JSON.parse(
9194
+ readFileSync(new URL('../../../ebk.repository-policy.json', import.meta.url), 'utf8'),
9195
+ );
9196
+ const workspaceManifest = JSON.parse(
9197
+ readFileSync(new URL('../../../package.json', import.meta.url), 'utf8'),
9198
+ );
9199
+ const rehearsalReceipt = JSON.parse(
9200
+ readFileSync(
9201
+ new URL(
9202
+ '../security/router-waf-blocking-rehearsal.json',
9203
+ import.meta.url,
9204
+ ),
9205
+ 'utf8',
9206
+ ),
9207
+ );
9208
+ const normalizedRehearsal = normalizeRouterWafBlockingRehearsal(rehearsalReceipt);
9209
+ if (normalizedRehearsal) {
9210
+ const rehearsalStage = productionRehearsalStage(repositoryPolicy.deploymentProfile);
9211
+ const rehearsalAccount = configuredDeploymentAccountForStage(
9212
+ repositoryPolicy.deploymentProfile,
9213
+ repositoryPolicy.deploymentAccounts,
9214
+ rehearsalStage,
9215
+ );
9216
+ const rehearsalRegion = configuredDeploymentRegion(repositoryPolicy.deploymentRegion);
9217
+ reconcileRouterWafBlockingRehearsal({
9218
+ evidence: JSON.parse(
9219
+ readFileSync(
9220
+ new URL('../security/router-waf-live-evidence.json', import.meta.url),
9221
+ 'utf8',
9222
+ ),
9223
+ ),
9224
+ expected: {
9225
+ accountId: rehearsalAccount,
9226
+ deploymentRoleArn: createStageAuthorityIdentity({
9227
+ accountId: rehearsalAccount,
9228
+ partition: rehearsalRegion.startsWith('us-gov-') ? 'aws-us-gov' : 'aws',
9229
+ region: rehearsalRegion,
9230
+ stage: rehearsalStage,
9231
+ }).deploymentRoleArn,
9232
+ region: rehearsalRegion,
9233
+ stage: rehearsalStage,
9234
+ wafContractDigest: routerWafContractDigest(routerWafPolicy),
9235
+ workspace: workspaceManifest.name,
9236
+ },
9237
+ policy: routerWafPolicy,
9238
+ receipt: normalizedRehearsal,
9239
+ });
9240
+ }
9241
+ const expectedCapabilities = ${JSON.stringify(foundationPlan.descriptor.capabilities)};
8411
9242
 
8412
- if (descriptor.contractVersion !== '1.0.0') {
8413
- throw new Error('Foundation contractVersion must be 1.0.0.');
9243
+ if (descriptor.contractVersion !== '2.0.0') {
9244
+ throw new Error('Foundation contractVersion must be 2.0.0.');
9245
+ }
9246
+ if (
9247
+ JSON.stringify(Object.keys(routerWafPolicy.stages).sort()) !==
9248
+ JSON.stringify(${JSON.stringify([...activeStages].sort())}) ||
9249
+ routerWafPolicy.stages.development.access !== 'allowlist' ||
9250
+ ${deploymentProfile !== 'starter' ? "routerWafPolicy.stages.staging?.access !== 'allowlist' ||" : ''}
9251
+ routerWafPolicy.stages.production.mode !== 'block'
9252
+ ) {
9253
+ throw new Error('Foundation Router WAF stage defaults are invalid.');
8414
9254
  }
8415
9255
  if (JSON.stringify(descriptor.capabilities) !== JSON.stringify(expectedCapabilities)) {
8416
- throw new Error('Foundation capability descriptor does not match the v1 standard.');
9256
+ throw new Error('Foundation capability descriptor does not match its generated resource declaration.');
8417
9257
  }
8418
9258
  if (
8419
- costs.schemaVersion !== 2 ||
9259
+ costs.schemaVersion !== 3 ||
9260
+ costs.aurora?.declared !== ${foundationPlan.resources.postgres} ||
8420
9261
  costs.aurora?.engineVersion !== '${index_js_4.FOUNDATION_AURORA_ENGINE_VERSION}' ||
8421
9262
  costs.productionProfileSource !== 'ebk.repository-policy.json' ||
8422
9263
  costs.productionProfiles?.standard?.aurora?.min !== '2 ACU' ||
8423
- costs.productionProfiles?.['cost-optimized']?.aurora?.min !== '0 ACU' ||
9264
+ JSON.stringify(costs.productionProfiles) !== JSON.stringify(${JSON.stringify(index_js_4.PRODUCTION_FOUNDATION_PROFILE_CONTRACTS)}) ||
8424
9265
  !costs.stages?.development ||
8425
- !costs.stages?.staging ||
9266
+ JSON.stringify(Object.keys(costs.stages ?? {}).sort()) !==
9267
+ JSON.stringify(${JSON.stringify([...activeStages].sort())}) ||
8426
9268
  costs.stages?.production?.dataRetention !== 'retained-and-protected'
8427
9269
  ) {
8428
- throw new Error('Foundation cost manifest does not match the v1 stage contract.');
9270
+ throw new Error('Foundation cost manifest does not match the declared capability and stage contract.');
8429
9271
  }
8430
9272
  const localHeaders =
8431
9273
  frontendSecurityHeaders.profiles?.['local-development']?.headers ?? [];
@@ -8462,9 +9304,9 @@ export {};
8462
9304
  export default $config({
8463
9305
  app(input: { stage?: string } | undefined) {
8464
9306
  const stage = input?.stage ?? 'development';
8465
- if (!['development', 'staging', 'production'].includes(stage)) {
9307
+ if (!new Set<string>(${JSON.stringify(activeStages)}).has(stage)) {
8466
9308
  throw new Error(
8467
- 'Foundation stage ' + stage + ' is not canonical. Use development, staging, or production.',
9309
+ 'Foundation stage ' + stage + ' is inactive for the ${deploymentProfile} deployment profile.',
8468
9310
  );
8469
9311
  }
8470
9312
  const production = stage === 'production';
@@ -8476,16 +9318,15 @@ export default $config({
8476
9318
  providers: {
8477
9319
  aws: {
8478
9320
  defaultTags: {
9321
+ // SST contributes sst:app and sst:stage. Keep the provider baseline
9322
+ // at eight tags so generated S3 asset objects remain within AWS's
9323
+ // hard ten-tag object limit. Richer performance tags stay explicit.
8479
9324
  tags: {
8480
9325
  'ebk:cost-allocation': 'foundation',
8481
9326
  'ebk:cost-scope': '${name}/foundation/' + stage,
8482
9327
  'ebk:framework-version': '${frameworkVersion}',
8483
9328
  'ebk:managed-by': 'empire-builder-kit',
8484
9329
  '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
9330
  'ebk:slice': 'foundation',
8490
9331
  'ebk:stage': stage,
8491
9332
  'ebk:workspace': '${name}',
@@ -8497,15 +9338,8 @@ export default $config({
8497
9338
  };
8498
9339
  },
8499
9340
  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
9341
  const { app } = await import('./infra/index.js');
8508
- return app(workloadRoles);
9342
+ return app();
8509
9343
  },
8510
9344
  });
8511
9345
  `,
@@ -8520,6 +9354,7 @@ export default $config({
8520
9354
  ],
8521
9355
  exclude: ['.sst', 'dist', 'node_modules'],
8522
9356
  }),
9357
+ [dependency_overrides_js_1.DEPENDENCY_OVERRIDES_PATH]: json(dependencyOverrides),
8523
9358
  'pnpm-workspace.yaml': `packages:
8524
9359
  - 'packages/*'
8525
9360
  - 'packages/*/apps/*'
@@ -8554,6 +9389,7 @@ supportedArchitectures:
8554
9389
  strictDepBuilds: true
8555
9390
  allowBuilds:
8556
9391
  ${index_js_6.FRAMEWORK_REVIEWED_BUILD_PACKAGES.map((packageName) => ` '${packageName}': true`).join('\n')}
9392
+ ${(0, dependency_overrides_js_1.renderDependencyOverridesYaml)(dependencyOverrides)}
8557
9393
  `,
8558
9394
  'tsconfig.base.json': json({
8559
9395
  compilerOptions: {
@@ -9376,7 +10212,9 @@ process.stdout.write(
9376
10212
  );
9377
10213
  `,
9378
10214
  'tools/ebk-git-policy.mjs': `import { evaluateGitPolicy } from '@empire-builder-kit/nx/git-policy';
10215
+ import { deliveryLaneOf, readRepositoryOperatingPolicy } from '@empire-builder-kit/nx/repository-policy';
9379
10216
  import { spawnSync } from 'node:child_process';
10217
+ const deliveryLane = deliveryLaneOf(readRepositoryOperatingPolicy(process.cwd()));
9380
10218
 
9381
10219
  const git = (...args) => {
9382
10220
  const result = spawnSync('git', args, { encoding: 'utf8', shell: false });
@@ -9396,32 +10234,40 @@ if (!['pull_request', 'push', 'workflow_dispatch'].includes(eventName)) {
9396
10234
  throw new Error('Unsupported or missing GITHUB_EVENT_NAME; Git policy fails closed.');
9397
10235
  }
9398
10236
 
9399
- const request = { eventName, ref: process.env.GITHUB_REF };
10237
+ const request = { deliveryLane, eventName, ref: process.env.GITHUB_REF };
9400
10238
  if (eventName === 'pull_request' || eventName === 'push') {
9401
10239
  git(
9402
10240
  'fetch',
9403
10241
  '--no-tags',
9404
10242
  'origin',
9405
10243
  'main:refs/remotes/origin/main',
9406
- 'develop:refs/remotes/origin/develop',
10244
+ ...(deliveryLane === 'trunk' ? [] : ['develop:refs/remotes/origin/develop']),
9407
10245
  );
9408
10246
  }
9409
10247
  if (eventName === 'pull_request') {
9410
10248
  const baseRef = process.env.GITHUB_BASE_REF;
10249
+ const headRepository = process.env.EBK_HEAD_REPOSITORY;
9411
10250
  const headRef = process.env.GITHUB_HEAD_REF;
9412
- if (!baseRef || !headRef) {
9413
- throw new Error('Pull-request branch metadata is missing.');
10251
+ const repository = process.env.GITHUB_REPOSITORY;
10252
+ if (!baseRef || !headRepository || !headRef || !repository) {
10253
+ throw new Error('Pull-request branch and repository metadata is missing.');
9414
10254
  }
9415
10255
  Object.assign(request, {
9416
10256
  baseIsAncestorOfHead: isAncestor('refs/remotes/origin/' + baseRef, 'HEAD'),
9417
10257
  baseRef,
10258
+ headRepository,
9418
10259
  headRef,
9419
- mainIsAncestorOfDevelop: isAncestor(
9420
- 'refs/remotes/origin/main',
9421
- 'refs/remotes/origin/develop',
9422
- ),
10260
+ ...(deliveryLane === 'trunk'
10261
+ ? {}
10262
+ : {
10263
+ mainIsAncestorOfDevelop: isAncestor(
10264
+ 'refs/remotes/origin/main',
10265
+ 'refs/remotes/origin/develop',
10266
+ ),
10267
+ }),
10268
+ repository,
9423
10269
  });
9424
- } else if (eventName === 'push') {
10270
+ } else if (eventName === 'push' && deliveryLane !== 'trunk') {
9425
10271
  Object.assign(request, {
9426
10272
  baseIsAncestorOfHead: isAncestor('refs/remotes/origin/develop', 'HEAD'),
9427
10273
  mainIsAncestorOfDevelop: isAncestor(
@@ -9435,7 +10281,7 @@ const decision = evaluateGitPolicy(request);
9435
10281
  process.stdout.write(JSON.stringify(decision, null, 2) + '\\n');
9436
10282
  if (!decision.allowed) process.exitCode = 1;
9437
10283
  `,
9438
- 'tools/workspace-health.mjs': `import { validateRepositoryOperatingPolicy } from '@empire-builder-kit/nx/repository-policy';
10284
+ 'tools/workspace-health.mjs': `import { readRepositoryOperatingPolicy } from '@empire-builder-kit/nx/repository-policy';
9439
10285
  import { assertWorkMarkerReport, formatWorkMarkerReport, scanWorkMarkers } from '@empire-builder-kit/nx/work-markers';
9440
10286
  import { createHash } from 'node:crypto';
9441
10287
  import { existsSync, readdirSync, readFileSync } from 'node:fs';
@@ -9498,13 +10344,7 @@ if (
9498
10344
  validateOwnership('.', workspace, 'Workspace');
9499
10345
  validateOwnership('packages/foundation', foundation, 'Foundation');
9500
10346
 
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
- }
10347
+ readRepositoryOperatingPolicy(process.cwd());
9508
10348
 
9509
10349
  for (const name of readdirSync('packages')) {
9510
10350
  if (name === 'foundation') continue;
@@ -9522,7 +10362,63 @@ assertWorkMarkerReport(markerReport);
9522
10362
  process.stdout.write('Empire Builder Kit workspace source health is green.\\n');
9523
10363
  `,
9524
10364
  };
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 }));
10365
+ 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)({
10366
+ deploymentProfile,
10367
+ name,
10368
+ workspaceDeploymentId,
10369
+ }));
10370
+ Object.assign(files, (0, workspace_files_ecr_inspector_js_1.renderThinEcrInspectorWorkflow)({
10371
+ actionPins: {
10372
+ checkout: checkoutAction,
10373
+ configureAwsCredentials: configureAwsAction,
10374
+ downloadArtifact: downloadArtifactAction,
10375
+ pnpmSetup: pnpmAction,
10376
+ setupNode: setupNodeAction,
10377
+ uploadArtifact: uploadArtifactAction,
10378
+ },
10379
+ deploymentProfile,
10380
+ name,
10381
+ nodeVersion: tool_versions_js_1.WORKSPACE_NODE_VERSION,
10382
+ }), (0, workspace_files_thin_deploy_js_1.renderThinDeployWorkflowFilePlan)({
10383
+ deliveryLane,
10384
+ productionApprovalMode,
10385
+ actionPins: {
10386
+ cache: cacheAction,
10387
+ checkout: checkoutAction,
10388
+ configureAwsCredentials: configureAwsAction,
10389
+ downloadArtifact: downloadArtifactAction,
10390
+ pnpmSetup: pnpmAction,
10391
+ setupNode: setupNodeAction,
10392
+ uploadArtifact: uploadArtifactAction,
10393
+ },
10394
+ deploymentProfile,
10395
+ name,
10396
+ toolVersions: {
10397
+ node: tool_versions_js_1.WORKSPACE_NODE_VERSION,
10398
+ sst: tool_versions_js_1.WORKSPACE_SST_VERSION,
10399
+ },
10400
+ }).files, (0, workspace_files_thin_docs_js_1.renderThinLifecycleDocumentation)({
10401
+ deliveryLane,
10402
+ deploymentProfile,
10403
+ name,
10404
+ productionApprovalMode,
10405
+ }));
10406
+ const thinReleaseWorkflowPlan = (0, workspace_files_thin_release_js_1.renderThinReleaseWorkflowPlan)();
10407
+ Object.assign(files, thinReleaseWorkflowPlan.files);
10408
+ for (const obsoletePath of [
10409
+ ...thinReleaseWorkflowPlan.obsoletePaths,
10410
+ // The trunk lane has no develop branch to sync main back into; trunk
10411
+ // promotion only ever fast-forwards main from develop, so there is
10412
+ // nothing to sync back either.
10413
+ ...(trunkLane || trunkPromotion ? ['.github/workflows/sync-back.yml'] : []),
10414
+ '.github/workflows/performance-aggregate-counts.yml',
10415
+ '.github/workflows/performance-cost-actual.yml',
10416
+ '.github/workflows/performance-load.yml',
10417
+ 'packages/foundation/operations/infrastructure-change.json',
10418
+ 'tools/ebk-infrastructure-plan-summary.mjs',
10419
+ ]) {
10420
+ delete files[obsoletePath];
10421
+ }
9526
10422
  for (const [path, source] of Object.entries(files)) {
9527
10423
  if (/^\.github\/workflows\/[^/]+\.ya?ml$/u.test(path)) {
9528
10424
  files[path] = isolateOidcTokenBeforeAwsAuthority(source, configureAwsAction);