@empire-builder-kit/nx 1.0.0-rc.30 → 1.0.0-rc.32

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 (310) hide show
  1. package/executors.json +8 -3
  2. package/package.json +14 -7
  3. package/src/aws/child-environment.d.ts +10 -0
  4. package/src/aws/child-environment.js +77 -0
  5. package/src/aws/child-environment.js.map +1 -0
  6. package/src/aws/identity.d.ts +13 -0
  7. package/src/aws/identity.js +41 -0
  8. package/src/aws/identity.js.map +1 -0
  9. package/src/aws-authority/index.d.ts +1 -0
  10. package/src/aws-authority/index.js +5 -0
  11. package/src/aws-authority/index.js.map +1 -0
  12. package/src/aws-authority/stage-authority.d.ts +240 -0
  13. package/src/aws-authority/stage-authority.js +2968 -0
  14. package/src/aws-authority/stage-authority.js.map +1 -0
  15. package/src/cli/main.d.ts +2 -0
  16. package/src/cli/main.js +11 -0
  17. package/src/cli/main.js.map +1 -1
  18. package/src/developer-agent/generator-planning.js +4 -4
  19. package/src/developer-agent/generator-planning.js.map +1 -1
  20. package/src/developer-agent/operation-registry.js +14 -6
  21. package/src/developer-agent/operation-registry.js.map +1 -1
  22. package/src/developer-agent/workspace-inspection.js +11 -2
  23. package/src/developer-agent/workspace-inspection.js.map +1 -1
  24. package/src/executors/deployment-plan/deployment-plan.js +1 -1
  25. package/src/executors/deployment-plan/deployment-plan.js.map +1 -1
  26. package/src/executors/foundation-preflight/foundation-preflight.d.ts +0 -1
  27. package/src/executors/foundation-preflight/foundation-preflight.js.map +1 -1
  28. package/src/executors/foundation-preflight/schema.json +0 -5
  29. package/src/executors/foundation-profile-preflight/foundation-profile-preflight.d.ts +12 -3
  30. package/src/executors/foundation-profile-preflight/foundation-profile-preflight.js +95 -32
  31. package/src/executors/foundation-profile-preflight/foundation-profile-preflight.js.map +1 -1
  32. package/src/executors/foundation-profile-preflight/production-database-safeguard.d.ts +64 -0
  33. package/src/executors/foundation-profile-preflight/production-database-safeguard.js +221 -0
  34. package/src/executors/foundation-profile-preflight/production-database-safeguard.js.map +1 -0
  35. package/src/executors/foundation-profile-preflight/schema.json +1 -6
  36. package/src/executors/foundation-remove-preflight/foundation-remove-preflight.d.ts +11 -1
  37. package/src/executors/foundation-remove-preflight/foundation-remove-preflight.js +44 -21
  38. package/src/executors/foundation-remove-preflight/foundation-remove-preflight.js.map +1 -1
  39. package/src/executors/foundation-remove-preflight/schema.json +6 -1
  40. package/src/executors/github-bootstrap/github-bootstrap.d.ts +1 -0
  41. package/src/executors/github-bootstrap/github-bootstrap.js +1 -0
  42. package/src/executors/github-bootstrap/github-bootstrap.js.map +1 -1
  43. package/src/executors/github-bootstrap/schema.json +7 -1
  44. package/src/executors/infrastructure-plan/infrastructure-plan.d.ts +27 -369
  45. package/src/executors/infrastructure-plan/infrastructure-plan.js +237 -5470
  46. package/src/executors/infrastructure-plan/infrastructure-plan.js.map +1 -1
  47. package/src/executors/infrastructure-plan/schema.json +8 -64
  48. package/src/executors/local-deploy/federated-credential-transport.d.ts +48 -0
  49. package/src/executors/local-deploy/federated-credential-transport.js +252 -0
  50. package/src/executors/local-deploy/federated-credential-transport.js.map +1 -0
  51. package/src/executors/local-deploy/local-approval.d.ts +4 -9
  52. package/src/executors/local-deploy/local-approval.js +3 -7
  53. package/src/executors/local-deploy/local-approval.js.map +1 -1
  54. package/src/executors/local-deploy/local-deploy.d.ts +60 -43
  55. package/src/executors/local-deploy/local-deploy.js +955 -804
  56. package/src/executors/local-deploy/local-deploy.js.map +1 -1
  57. package/src/executors/local-deploy/schema.json +3 -20
  58. package/src/executors/refresh/refresh.js +2 -2
  59. package/src/executors/refresh/refresh.js.map +1 -1
  60. package/src/executors/remove/remove.js +2 -2
  61. package/src/executors/remove/remove.js.map +1 -1
  62. package/src/executors/router-waf-rehearsal/router-waf-rehearsal.d.ts +27 -1
  63. package/src/executors/router-waf-rehearsal/router-waf-rehearsal.js +907 -22
  64. package/src/executors/router-waf-rehearsal/router-waf-rehearsal.js.map +1 -1
  65. package/src/executors/router-waf-rehearsal/schema.json +17 -2
  66. package/src/executors/secret-initialize/schema.json +42 -0
  67. package/src/executors/secret-initialize/secret-initialize.d.ts +32 -0
  68. package/src/executors/secret-initialize/secret-initialize.js +243 -0
  69. package/src/executors/secret-initialize/secret-initialize.js.map +1 -0
  70. package/src/executors/secret-metadata/secret-metadata.d.ts +4 -1
  71. package/src/executors/secret-metadata/secret-metadata.js +76 -29
  72. package/src/executors/secret-metadata/secret-metadata.js.map +1 -1
  73. package/src/executors/sst-lifecycle/generated-secret-retirement.d.ts +53 -0
  74. package/src/executors/sst-lifecycle/generated-secret-retirement.js +192 -0
  75. package/src/executors/sst-lifecycle/generated-secret-retirement.js.map +1 -0
  76. package/src/executors/sst-lifecycle/governed-native-operation.d.ts +46 -0
  77. package/src/executors/sst-lifecycle/governed-native-operation.js +153 -0
  78. package/src/executors/sst-lifecycle/governed-native-operation.js.map +1 -0
  79. package/src/executors/sst-lifecycle/run-sst-command.d.ts +27 -7
  80. package/src/executors/sst-lifecycle/run-sst-command.js +474 -89
  81. package/src/executors/sst-lifecycle/run-sst-command.js.map +1 -1
  82. package/src/executors/sst-lifecycle/schema.json +0 -5
  83. package/src/executors/sst-lifecycle/secret-preflight.d.ts +2 -0
  84. package/src/executors/sst-lifecycle/secret-preflight.js +13 -7
  85. package/src/executors/sst-lifecycle/secret-preflight.js.map +1 -1
  86. package/src/executors/unlock/unlock.js +2 -2
  87. package/src/executors/unlock/unlock.js.map +1 -1
  88. package/src/foundation/provision-logical-database.js +2 -0
  89. package/src/foundation/provision-logical-database.js.map +1 -1
  90. package/src/foundation/router-waf-rate-rehearsal.d.ts +97 -0
  91. package/src/foundation/router-waf-rate-rehearsal.js +210 -0
  92. package/src/foundation/router-waf-rate-rehearsal.js.map +1 -0
  93. package/src/foundation/router-waf.d.ts +59 -9
  94. package/src/foundation/router-waf.js +427 -32
  95. package/src/foundation/router-waf.js.map +1 -1
  96. package/src/foundation/stages.d.ts +1 -0
  97. package/src/foundation/stages.js +11 -0
  98. package/src/foundation/stages.js.map +1 -1
  99. package/src/generators/formatting.d.ts +8 -0
  100. package/src/generators/formatting.js +118 -0
  101. package/src/generators/formatting.js.map +1 -0
  102. package/src/generators/function/function.js +37 -17
  103. package/src/generators/function/function.js.map +1 -1
  104. package/src/generators/post-generation-install.js +2 -2
  105. package/src/generators/post-generation-install.js.map +1 -1
  106. package/src/generators/preset/generator.js +28 -8
  107. package/src/generators/preset/generator.js.map +1 -1
  108. package/src/generators/preset/schema.d.ts +3 -0
  109. package/src/generators/preset/schema.json +5 -5
  110. package/src/generators/preset/workspace-files-developer-agent.js +15 -7
  111. package/src/generators/preset/workspace-files-developer-agent.js.map +1 -1
  112. package/src/generators/preset/workspace-files-ecr-inspector.d.ts +18 -0
  113. package/src/generators/preset/workspace-files-ecr-inspector.js +298 -0
  114. package/src/generators/preset/workspace-files-ecr-inspector.js.map +1 -0
  115. package/src/generators/preset/workspace-files-performance-cost.d.ts +2 -2
  116. package/src/generators/preset/workspace-files-performance-cost.js +8 -8
  117. package/src/generators/preset/workspace-files-performance-cost.js.map +1 -1
  118. package/src/generators/preset/workspace-files-performance-load.d.ts +1 -1
  119. package/src/generators/preset/workspace-files-performance-load.js +29 -24
  120. package/src/generators/preset/workspace-files-performance-load.js.map +1 -1
  121. package/src/generators/preset/workspace-files-stage-authority.d.ts +6 -0
  122. package/src/generators/preset/workspace-files-stage-authority.js +479 -0
  123. package/src/generators/preset/workspace-files-stage-authority.js.map +1 -0
  124. package/src/generators/preset/workspace-files-thin-deploy.d.ts +40 -0
  125. package/src/generators/preset/workspace-files-thin-deploy.js +1500 -0
  126. package/src/generators/preset/workspace-files-thin-deploy.js.map +1 -0
  127. package/src/generators/preset/workspace-files-thin-docs.d.ts +5 -0
  128. package/src/generators/preset/workspace-files-thin-docs.js +240 -0
  129. package/src/generators/preset/workspace-files-thin-docs.js.map +1 -0
  130. package/src/generators/preset/workspace-files-thin-release.d.ts +14 -0
  131. package/src/generators/preset/workspace-files-thin-release.js +26 -0
  132. package/src/generators/preset/workspace-files-thin-release.js.map +1 -0
  133. package/src/generators/preset/workspace-files.d.ts +2 -1
  134. package/src/generators/preset/workspace-files.js +448 -680
  135. package/src/generators/preset/workspace-files.js.map +1 -1
  136. package/src/generators/repository-policy/repository-policy.js +149 -19
  137. package/src/generators/repository-policy/repository-policy.js.map +1 -1
  138. package/src/generators/repository-policy/schema.d.ts +2 -0
  139. package/src/generators/repository-policy/schema.json +11 -1
  140. package/src/generators/repository-policy/tree-policy.d.ts +3 -0
  141. package/src/generators/repository-policy/tree-policy.js +25 -0
  142. package/src/generators/repository-policy/tree-policy.js.map +1 -0
  143. package/src/generators/slice/built-in-plan-api-files.js +1 -0
  144. package/src/generators/slice/built-in-plan-api-files.js.map +1 -1
  145. package/src/generators/slice/built-in-plan-async-work-files.js +41 -0
  146. package/src/generators/slice/built-in-plan-async-work-files.js.map +1 -1
  147. package/src/generators/slice/built-in-plan-authorization-infrastructure-files.js +6 -0
  148. package/src/generators/slice/built-in-plan-authorization-infrastructure-files.js.map +1 -1
  149. package/src/generators/slice/built-in-plan-data-export-infrastructure-files.js +5 -31
  150. package/src/generators/slice/built-in-plan-data-export-infrastructure-files.js.map +1 -1
  151. package/src/generators/slice/built-in-plan-data-files.d.ts +1 -1
  152. package/src/generators/slice/built-in-plan-data-files.js +89 -30
  153. package/src/generators/slice/built-in-plan-data-files.js.map +1 -1
  154. package/src/generators/slice/built-in-plan-data-lifecycle-runtime-files.js +19 -0
  155. package/src/generators/slice/built-in-plan-data-lifecycle-runtime-files.js.map +1 -1
  156. package/src/generators/slice/built-in-plan-data-retention-launcher-files.js +21 -3
  157. package/src/generators/slice/built-in-plan-data-retention-launcher-files.js.map +1 -1
  158. package/src/generators/slice/built-in-plan-database-retirement-rehearsal-files.js +20 -1
  159. package/src/generators/slice/built-in-plan-database-retirement-rehearsal-files.js.map +1 -1
  160. package/src/generators/slice/built-in-plan-foundation-files.js +3 -5
  161. package/src/generators/slice/built-in-plan-foundation-files.js.map +1 -1
  162. package/src/generators/slice/built-in-plan-frontend-files.js +5 -1
  163. package/src/generators/slice/built-in-plan-frontend-files.js.map +1 -1
  164. package/src/generators/slice/built-in-plan-infrastructure-files.d.ts +1 -1
  165. package/src/generators/slice/built-in-plan-infrastructure-files.js +199 -188
  166. package/src/generators/slice/built-in-plan-infrastructure-files.js.map +1 -1
  167. package/src/generators/slice/built-in-plan-metadata-files.js +13 -0
  168. package/src/generators/slice/built-in-plan-metadata-files.js.map +1 -1
  169. package/src/generators/slice/built-in-plan-native-runtime-authority-files.d.ts +2 -0
  170. package/src/generators/slice/built-in-plan-native-runtime-authority-files.js +307 -0
  171. package/src/generators/slice/built-in-plan-native-runtime-authority-files.js.map +1 -0
  172. package/src/generators/slice/built-in-plan-operations-files.js +65 -51
  173. package/src/generators/slice/built-in-plan-operations-files.js.map +1 -1
  174. package/src/generators/slice/built-in-plan-performance-files.js +97 -19
  175. package/src/generators/slice/built-in-plan-performance-files.js.map +1 -1
  176. package/src/generators/slice/built-in-plan-project-files.js +9 -68
  177. package/src/generators/slice/built-in-plan-project-files.js.map +1 -1
  178. package/src/generators/slice/built-in-plan-secret-runtime-files.d.ts +1 -1
  179. package/src/generators/slice/built-in-plan-secret-runtime-files.js +87 -76
  180. package/src/generators/slice/built-in-plan-secret-runtime-files.js.map +1 -1
  181. package/src/generators/slice/built-in-plan-testing-files.js +94 -47
  182. package/src/generators/slice/built-in-plan-testing-files.js.map +1 -1
  183. package/src/generators/slice/built-in-plan-thin-release-files.d.ts +11 -0
  184. package/src/generators/slice/built-in-plan-thin-release-files.js +129 -0
  185. package/src/generators/slice/built-in-plan-thin-release-files.js.map +1 -0
  186. package/src/generators/slice/built-in-plan-verification-files.js +6 -2
  187. package/src/generators/slice/built-in-plan-verification-files.js.map +1 -1
  188. package/src/generators/slice/built-in-plan-web-files.js +4 -2
  189. package/src/generators/slice/built-in-plan-web-files.js.map +1 -1
  190. package/src/generators/slice/built-in-plan-workload-role-files.js +26 -4
  191. package/src/generators/slice/built-in-plan-workload-role-files.js.map +1 -1
  192. package/src/generators/slice/built-in-plan.d.ts +1 -0
  193. package/src/generators/slice/built-in-plan.js +28 -11
  194. package/src/generators/slice/built-in-plan.js.map +1 -1
  195. package/src/generators/slice/slice.js +30 -32
  196. package/src/generators/slice/slice.js.map +1 -1
  197. package/src/generators/upgrade/upgrade.d.ts +1 -1
  198. package/src/generators/upgrade/upgrade.js +200 -48
  199. package/src/generators/upgrade/upgrade.js.map +1 -1
  200. package/src/generators/workload/performance-registration.d.ts +1 -0
  201. package/src/generators/workload/performance-registration.js +9 -11
  202. package/src/generators/workload/performance-registration.js.map +1 -1
  203. package/src/generators/workload/service-performance-files.d.ts +1 -0
  204. package/src/generators/workload/service-performance-files.js +27 -7
  205. package/src/generators/workload/service-performance-files.js.map +1 -1
  206. package/src/generators/workload/workload.d.ts +1 -1
  207. package/src/generators/workload/workload.js +55 -28
  208. package/src/generators/workload/workload.js.map +1 -1
  209. package/src/git-policy/github-bootstrap.d.ts +28 -7
  210. package/src/git-policy/github-bootstrap.js +205 -163
  211. package/src/git-policy/github-bootstrap.js.map +1 -1
  212. package/src/index.d.ts +1 -0
  213. package/src/index.js +1 -0
  214. package/src/index.js.map +1 -1
  215. package/src/local-dev/private-state.d.ts +3 -0
  216. package/src/local-dev/private-state.js +17 -5
  217. package/src/local-dev/private-state.js.map +1 -1
  218. package/src/local-dev/tls.js +5 -1
  219. package/src/local-dev/tls.js.map +1 -1
  220. package/src/performance/cost-collection.d.ts +1 -1
  221. package/src/performance/cost-collection.js +4 -2
  222. package/src/performance/cost-collection.js.map +1 -1
  223. package/src/performance/cost-controls.d.ts +3 -1
  224. package/src/performance/cost-controls.js +9 -4
  225. package/src/performance/cost-controls.js.map +1 -1
  226. package/src/performance/evidence.d.ts +1 -1
  227. package/src/performance/evidence.js +5 -3
  228. package/src/performance/evidence.js.map +1 -1
  229. package/src/performance/load-safety.js +5 -3
  230. package/src/performance/load-safety.js.map +1 -1
  231. package/src/performance/model.d.ts +2 -2
  232. package/src/performance/model.js +4 -3
  233. package/src/performance/model.js.map +1 -1
  234. package/src/release/index.d.ts +0 -2
  235. package/src/release/index.js +0 -2
  236. package/src/release/index.js.map +1 -1
  237. package/src/release/source-authority.d.ts +4 -5
  238. package/src/release/source-authority.js +13 -123
  239. package/src/release/source-authority.js.map +1 -1
  240. package/src/repository-policy/aurora-preflight.d.ts +1 -1
  241. package/src/repository-policy/aurora-preflight.js +4 -1
  242. package/src/repository-policy/aurora-preflight.js.map +1 -1
  243. package/src/repository-policy/policy.d.ts +21 -2
  244. package/src/repository-policy/policy.js +290 -25
  245. package/src/repository-policy/policy.js.map +1 -1
  246. package/src/schemas/performance-policy-v1.json +3 -1
  247. package/src/schemas/repository-policy-v1.json +46 -2
  248. package/src/secrets/adapters.d.ts +7 -2
  249. package/src/secrets/adapters.js +34 -3
  250. package/src/secrets/adapters.js.map +1 -1
  251. package/src/secrets/aws-control-plane.d.ts +10 -1
  252. package/src/secrets/aws-control-plane.js +67 -0
  253. package/src/secrets/aws-control-plane.js.map +1 -1
  254. package/src/secrets/generated-aws-control-plane.d.ts +23 -0
  255. package/src/secrets/generated-aws-control-plane.js +259 -0
  256. package/src/secrets/generated-aws-control-plane.js.map +1 -0
  257. package/src/secrets/metadata.js +38 -10
  258. package/src/secrets/metadata.js.map +1 -1
  259. package/src/secrets/model.d.ts +8 -1
  260. package/src/secrets/model.js +24 -3
  261. package/src/secrets/model.js.map +1 -1
  262. package/src/secrets/operator.d.ts +89 -3
  263. package/src/secrets/operator.js +422 -9
  264. package/src/secrets/operator.js.map +1 -1
  265. package/src/secrets/registry.d.ts +2 -1
  266. package/src/secrets/registry.js +18 -1
  267. package/src/secrets/registry.js.map +1 -1
  268. package/src/secrets/workload-access.js +10 -2
  269. package/src/secrets/workload-access.js.map +1 -1
  270. package/src/supply-chain/action-pins.js +61 -7
  271. package/src/supply-chain/action-pins.js.map +1 -1
  272. package/src/supply-chain/build-output-bounds.d.ts +2 -0
  273. package/src/supply-chain/build-output-bounds.js +12 -0
  274. package/src/supply-chain/build-output-bounds.js.map +1 -0
  275. package/src/supply-chain/build-output.js +2 -4
  276. package/src/supply-chain/build-output.js.map +1 -1
  277. package/src/supply-chain/ecr-scanning.d.ts +5 -3
  278. package/src/supply-chain/ecr-scanning.js +11 -7
  279. package/src/supply-chain/ecr-scanning.js.map +1 -1
  280. package/src/supply-chain/ecs-deployed-image.d.ts +5 -3
  281. package/src/supply-chain/ecs-deployed-image.js +6 -4
  282. package/src/supply-chain/ecs-deployed-image.js.map +1 -1
  283. package/src/supply-chain/generated-container-release-command.js +849 -44
  284. package/src/supply-chain/generated-container-release-command.js.map +1 -1
  285. package/src/supply-chain/generated-ecr-collector-command.d.ts +0 -1
  286. package/src/supply-chain/generated-ecr-collector-command.js +188 -91
  287. package/src/supply-chain/generated-ecr-collector-command.js.map +1 -1
  288. package/src/supply-chain/generated-files.js +4 -3
  289. package/src/supply-chain/generated-files.js.map +1 -1
  290. package/src/supply-chain/index.d.ts +1 -1
  291. package/src/supply-chain/index.js +2 -1
  292. package/src/supply-chain/index.js.map +1 -1
  293. package/src/workload-roles/index.d.ts +10 -17
  294. package/src/workload-roles/index.js +5 -18
  295. package/src/workload-roles/index.js.map +1 -1
  296. package/src/workspace-deployment/identity.d.ts +3 -0
  297. package/src/workspace-deployment/identity.js +22 -0
  298. package/src/workspace-deployment/identity.js.map +1 -0
  299. package/src/workspace-deployment/index.d.ts +3 -0
  300. package/src/workspace-deployment/index.js +7 -0
  301. package/src/workspace-deployment/index.js.map +1 -0
  302. package/src/workspace-deployment/migration-evidence.d.ts +29 -0
  303. package/src/workspace-deployment/migration-evidence.js +83 -0
  304. package/src/workspace-deployment/migration-evidence.js.map +1 -0
  305. package/src/workspace-deployment/migration.d.ts +27 -0
  306. package/src/workspace-deployment/migration.js +109 -0
  307. package/src/workspace-deployment/migration.js.map +1 -0
  308. package/src/workspace-deployment/ownership.d.ts +81 -0
  309. package/src/workspace-deployment/ownership.js +381 -0
  310. package/src/workspace-deployment/ownership.js.map +1 -0
@@ -17,10 +17,11 @@ const deterministic_order_js_1 = require("../../deterministic-order.js");
17
17
  const tool_versions_js_1 = require("./tool-versions.js");
18
18
  const workspace_files_developer_agent_js_1 = require("./workspace-files-developer-agent.js");
19
19
  const workspace_files_ci_acceptance_js_1 = require("./workspace-files-ci-acceptance.js");
20
- const workspace_files_foundation_workload_roles_js_1 = require("./workspace-files-foundation-workload-roles.js");
21
- const workspace_files_performance_cost_js_1 = require("./workspace-files-performance-cost.js");
22
- const workspace_files_performance_load_js_1 = require("./workspace-files-performance-load.js");
23
- const workspace_files_workload_role_baseline_js_1 = require("./workspace-files-workload-role-baseline.js");
20
+ const workspace_files_ecr_inspector_js_1 = require("./workspace-files-ecr-inspector.js");
21
+ const workspace_files_thin_deploy_js_1 = require("./workspace-files-thin-deploy.js");
22
+ const workspace_files_thin_docs_js_1 = require("./workspace-files-thin-docs.js");
23
+ const workspace_files_thin_release_js_1 = require("./workspace-files-thin-release.js");
24
+ const workspace_files_stage_authority_js_1 = require("./workspace-files-stage-authority.js");
24
25
  const GENERATED_CREDENTIAL_SHAPE_PATTERNS = `[${sensitive_patterns_1.CREDENTIAL_SHAPE_DEFINITIONS.map(({ pattern }) => `new RegExp(${JSON.stringify(pattern.source)}, ${JSON.stringify(pattern.flags)})`).join(',')}]`;
25
26
  const GENERATED_RELEASE_ROLLOUT_PLAN_ID_PATTERN = JSON.stringify(index_js_5.RELEASE_ROLLOUT_PLAN_ID_PATTERN_SOURCE);
26
27
  exports.BLUEPRINT_TRUST_AUTHORITY_PATH = 'tools/blueprint-trust-check.mjs';
@@ -172,6 +173,49 @@ const TOOL_VERSIONS = {
172
173
  function json(value) {
173
174
  return `${JSON.stringify(value, null, 2)}\n`;
174
175
  }
176
+ function indentWorkflowScript(source, spaces) {
177
+ const prefix = ' '.repeat(spaces);
178
+ return source
179
+ .trim()
180
+ .split('\n')
181
+ .map((line) => `${prefix}${line}`)
182
+ .join('\n');
183
+ }
184
+ function protectedSecretPolicyDerivationScript() {
185
+ return `node --input-type=module <<'NODE'
186
+ import { appendFileSync } from 'node:fs';
187
+ import {
188
+ configuredDeploymentAccountTopology,
189
+ configuredDeploymentRegion,
190
+ isActiveDeploymentStage,
191
+ readRepositoryOperatingPolicy,
192
+ } from '@empire-builder-kit/nx/repository-policy';
193
+
194
+ const output = process.env.GITHUB_OUTPUT?.trim() ?? '';
195
+ const renderedProfile = process.env.EBK_RENDERED_PROFILE?.trim() ?? '';
196
+ const stage = process.env.EBK_STAGE?.trim() ?? '';
197
+ if (!output) throw new Error('Secret workflow authority output is unavailable.');
198
+ const policy = readRepositoryOperatingPolicy(process.cwd());
199
+ if (policy.deploymentProfile !== renderedProfile) {
200
+ throw new Error('Generated and committed deployment profiles differ.');
201
+ }
202
+ if (!isActiveDeploymentStage(policy.deploymentProfile, stage)) {
203
+ throw new Error('Secret workflow stage is inactive for the committed deployment profile.');
204
+ }
205
+ const accounts = configuredDeploymentAccountTopology(
206
+ policy.deploymentProfile,
207
+ policy.deploymentAccounts,
208
+ );
209
+ const region = configuredDeploymentRegion(policy.deploymentRegion);
210
+ appendFileSync(output, [
211
+ 'deployment_region=' + region,
212
+ 'development_account_id=' + accounts.development,
213
+ 'staging_account_id=' + (accounts.staging ?? ''),
214
+ 'production_account_id=' + accounts.production,
215
+ '',
216
+ ].join('\\n'));
217
+ NODE`;
218
+ }
175
219
  function planApplicationBindingCheck() {
176
220
  return ` node - <<'NODE'
177
221
  const declared = JSON.parse(process.env.AWS_PLAN_SST_APPS_JSON ?? '');
@@ -1015,6 +1059,8 @@ const WORKSPACE_SEEDED_PATHS = new Set([
1015
1059
  'docs/architecture.md',
1016
1060
  'docs/git-workflow.md',
1017
1061
  'docs/repository-operating-policy.md',
1062
+ 'docs/stage-deployment-authority.md',
1063
+ 'docs/workspace-deployment-ownership.md',
1018
1064
  'docs/work-markers.md',
1019
1065
  'ebk.repository-policy.json',
1020
1066
  ]);
@@ -1042,14 +1088,13 @@ exports.WORKSPACE_PROJECT_MANAGED_JSON_POINTERS = [
1042
1088
  '/targets/dev-setup',
1043
1089
  '/targets/github-bootstrap',
1044
1090
  '/targets/install-git-hooks',
1091
+ '/targets/stage-authority-render',
1045
1092
  '/targets/supply-chain-check',
1046
1093
  '/targets/supply-chain-check-protected',
1047
1094
  '/targets/supply-chain-scan',
1048
1095
  '/targets/verify',
1049
1096
  '/targets/verify-proposed',
1050
1097
  '/targets/work-markers',
1051
- '/targets/workload-role-candidate-report',
1052
- '/targets/workload-role-provisioner-build',
1053
1098
  '/targets/workspace-health',
1054
1099
  ];
1055
1100
  const FOUNDATION_SEEDED_PATHS = new Set([
@@ -1124,7 +1169,20 @@ function createWorkspaceOwnership(files, frameworkVersion) {
1124
1169
  function renderWorkspaceFiles(options) {
1125
1170
  const { frameworkVersion, name } = options;
1126
1171
  const deploymentProfile = options.deploymentProfile ?? 'starter';
1127
- const initialRepositoryPolicy = (0, index_js_4.createIncubationRepositoryPolicy)(deploymentProfile);
1172
+ const activeStages = (0, index_js_4.activeDeploymentStages)(deploymentProfile);
1173
+ const protectedWorkflowStageChoices = deploymentProfile === 'starter'
1174
+ ? 'development, production'
1175
+ : deploymentProfile === 'production-isolated'
1176
+ ? 'production'
1177
+ : 'staging, production';
1178
+ const ecrInspectorWorkflowStageChoices = deploymentProfile === 'starter'
1179
+ ? 'production'
1180
+ : protectedWorkflowStageChoices;
1181
+ const rehearsalStage = (0, index_js_4.productionRehearsalStage)(deploymentProfile);
1182
+ const protectedWorkflowStageBranchCases = `${rehearsalStage}:refs/heads/develop|production:refs/heads/main`;
1183
+ const workspaceDeploymentId = options.workspaceDeploymentId ??
1184
+ `ebkws-${(0, index_js_1.hashGeneratedContent)(`workspace-deployment-id:${name}`).slice(0, 32)}`;
1185
+ const initialRepositoryPolicy = (0, index_js_4.createIncubationRepositoryPolicy)(deploymentProfile, workspaceDeploymentId);
1128
1186
  initialRepositoryPolicy.deploymentAccounts = options.deploymentAccounts ?? {
1129
1187
  development: null,
1130
1188
  production: null,
@@ -1367,6 +1425,7 @@ jobs:
1367
1425
  include-hidden-files: true
1368
1426
  retention-days: 30
1369
1427
  path: .ebk/evidence/release-candidate
1428
+ ${(0, workspace_files_thin_deploy_js_1.renderThinDeployVerifyCaller)(deploymentProfile)}
1370
1429
  `,
1371
1430
  '.github/workflows/deploy.yml': `name: Deploy
1372
1431
 
@@ -3876,7 +3935,7 @@ on:
3876
3935
  description: Exact protected deployment stage
3877
3936
  required: true
3878
3937
  type: choice
3879
- options: [staging, production]
3938
+ options: [${ecrInspectorWorkflowStageChoices}]
3880
3939
  deployment_commit:
3881
3940
  description: Exact successful protected deployment commit
3882
3941
  required: true
@@ -3885,8 +3944,8 @@ on:
3885
3944
  description: Exact successful Deploy workflow run ID
3886
3945
  required: true
3887
3946
  type: string
3888
- release_artifact_name:
3889
- description: Exact release-evidence artifact from that run
3947
+ deployment_receipt_artifact_name:
3948
+ description: Exact deployment-receipt artifact from that run
3890
3949
  required: true
3891
3950
  type: string
3892
3951
  slice:
@@ -3940,7 +3999,7 @@ jobs:
3940
3999
  EBK_DEPLOYMENT_ARN: \${{ inputs.deployment_arn }}
3941
4000
  EBK_DEPLOYMENT_KIND: \${{ inputs.deployment_kind }}
3942
4001
  EBK_DEPLOY_RUN_ID: \${{ inputs.deploy_run_id }}
3943
- EBK_RELEASE_ARTIFACT_NAME: \${{ inputs.release_artifact_name }}
4002
+ EBK_DEPLOYMENT_RECEIPT_ARTIFACT_NAME: \${{ inputs.deployment_receipt_artifact_name }}
3944
4003
  EBK_SLICE: \${{ inputs.slice }}
3945
4004
  EBK_DEPLOYMENT_COMMIT: \${{ inputs.deployment_commit }}
3946
4005
  EBK_STAGE: \${{ inputs.stage }}
@@ -3963,10 +4022,10 @@ jobs:
3963
4022
  esac
3964
4023
  expected_branch=develop
3965
4024
  if [ "$EBK_STAGE" = production ]; then expected_branch=main; fi
3966
- expected_artifact="release-evidence-$EBK_STAGE-$EBK_DEPLOY_RUN_ID-"
3967
- case "$EBK_RELEASE_ARTIFACT_NAME" in
3968
- "$expected_artifact"*) attempt="\${EBK_RELEASE_ARTIFACT_NAME#"$expected_artifact"}" ;;
3969
- *) echo 'Release evidence artifact does not bind the exact deploy run'; exit 1 ;;
4025
+ expected_artifact="deployment-receipt-$EBK_STAGE-$EBK_DEPLOY_RUN_ID-"
4026
+ case "$EBK_DEPLOYMENT_RECEIPT_ARTIFACT_NAME" in
4027
+ "$expected_artifact"*) attempt="\${EBK_DEPLOYMENT_RECEIPT_ARTIFACT_NAME#"$expected_artifact"}" ;;
4028
+ *) echo 'Deployment receipt artifact does not bind the exact deploy run'; exit 1 ;;
3970
4029
  esac
3971
4030
  case "$attempt" in
3972
4031
  ''|0*|*[!0-9]*) echo 'Release evidence artifact has an invalid run attempt'; exit 1 ;;
@@ -3979,7 +4038,7 @@ jobs:
3979
4038
  test "$(printf '%s' "$run" | jq -r .path)" = ".github/workflows/deploy.yml" ||
3980
4039
  { echo 'Deploy run is not the exact successful protected source authority'; exit 1; }
3981
4040
  artifacts="$(gh api "/repos/$GH_REPO/actions/runs/$EBK_DEPLOY_RUN_ID/artifacts?per_page=100")"
3982
- count="$(printf '%s' "$artifacts" | jq --arg name "$EBK_RELEASE_ARTIFACT_NAME" \
4041
+ count="$(printf '%s' "$artifacts" | jq --arg name "$EBK_DEPLOYMENT_RECEIPT_ARTIFACT_NAME" \
3983
4042
  '[.artifacts[] | select(.name == $name and .expired == false)] | length')"
3984
4043
  test "$count" = 1 ||
3985
4044
  { echo 'Exact unexpired release evidence artifact is absent or ambiguous'; exit 1; }
@@ -3996,7 +4055,7 @@ jobs:
3996
4055
  - uses: ${downloadArtifactAction}
3997
4056
  with:
3998
4057
  github-token: \${{ github.token }}
3999
- name: \${{ inputs.release_artifact_name }}
4058
+ name: \${{ inputs.deployment_receipt_artifact_name }}
4000
4059
  path: .
4001
4060
  run-id: \${{ inputs.deploy_run_id }}
4002
4061
  - uses: ${pnpmAction}
@@ -4103,7 +4162,7 @@ jobs:
4103
4162
  { encoding: "utf8" },
4104
4163
  ).trim()
4105
4164
  ) fail("Production deployment tree differs from its reviewed release source.");
4106
- } else if (stage !== "staging") {
4165
+ } else if (stage !== "${rehearsalStage}") {
4107
4166
  fail("Protected release stage is invalid.");
4108
4167
  }
4109
4168
  const productionKeys = [
@@ -4159,7 +4218,7 @@ jobs:
4159
4218
  !digest.test(binding.candidateDigest ?? "") ||
4160
4219
  (stage === "production" &&
4161
4220
  !digest.test(binding.lifecycleDigest ?? "")) ||
4162
- (stage === "staging" && binding.stage !== stage) ||
4221
+ (stage === "${rehearsalStage}" && binding.stage !== stage) ||
4163
4222
  binding.runId !== process.env.EBK_DEPLOY_RUN_ID ||
4164
4223
  binding.runAttempt !== process.env.EBK_DEPLOY_RUN_ATTEMPT ||
4165
4224
  binding.workflow !== expectedWorkflow
@@ -4238,7 +4297,7 @@ on:
4238
4297
  description: Protected deployed stage
4239
4298
  required: true
4240
4299
  type: choice
4241
- options: [staging, production]
4300
+ options: [${protectedWorkflowStageChoices}]
4242
4301
  project:
4243
4302
  description: Exact Product Slice project
4244
4303
  required: true
@@ -4338,7 +4397,7 @@ jobs:
4338
4397
  EBK_REPORT_RUN_ID: \${{ inputs.report_run_id }}
4339
4398
  run: |
4340
4399
  case "$EBK_STAGE:$EBK_PROTECTED_REF" in
4341
- staging:refs/heads/develop|production:refs/heads/main) ;;
4400
+ ${protectedWorkflowStageBranchCases}) ;;
4342
4401
  *) echo 'Async recovery stage does not match its protected branch'; exit 1 ;;
4343
4402
  esac
4344
4403
  case "$EBK_PROJECT" in
@@ -5190,27 +5249,26 @@ jobs:
5190
5249
  );
5191
5250
  NODE
5192
5251
  - id: classify
5193
- name: Require one successful staging deployment job
5252
+ name: Require one successful thin staging deployment job
5194
5253
  env:
5195
5254
  EBK_DEPLOYMENT_PROFILE: \${{ steps.profile.outputs.profile }}
5196
5255
  EBK_DEPLOYMENT_RUN_ID: \${{ github.event.workflow_run.id }}
5197
5256
  GH_TOKEN: \${{ github.token }}
5198
5257
  GH_REPO: \${{ github.repository }}
5199
5258
  run: |
5259
+ if [ "$EBK_DEPLOYMENT_PROFILE" = starter ]; then
5260
+ echo 'deployed=false' >> "$GITHUB_OUTPUT"
5261
+ exit 0
5262
+ fi
5200
5263
  jobs="$(gh api \
5201
5264
  "/repos/$GH_REPO/actions/runs/$EBK_DEPLOYMENT_RUN_ID/jobs?filter=latest&per_page=100")"
5202
- apply_count="$(printf '%s' "$jobs" | jq \
5203
- '[.jobs[] | select(.name | startswith("${stagingApplyJobPrefix}")) | select(.conclusion == "success")] | length')"
5204
- route_success_count="$(printf '%s' "$jobs" | jq \
5205
- '[.jobs[] | select(.name == "${protectedDeploymentRouteJobName}" and .conclusion == "success")] | length')"
5206
- route_skipped_count="$(printf '%s' "$jobs" | jq \
5207
- '[.jobs[] | select(.name == "${protectedDeploymentRouteJobName}" and .conclusion == "skipped")] | length')"
5208
- case "$apply_count:$route_success_count:$route_skipped_count:$EBK_DEPLOYMENT_PROFILE" in
5209
- 1:1:0:*) echo 'deployed=true' >> "$GITHUB_OUTPUT" ;;
5210
- 0:0:1:*) echo 'deployed=false' >> "$GITHUB_OUTPUT" ;;
5211
- 0:1:0:starter) echo 'deployed=false' >> "$GITHUB_OUTPUT" ;;
5212
- *) echo 'Deploy route and staging application evidence disagree'; exit 1 ;;
5213
- esac
5265
+ execute_count="$(printf '%s' "$jobs" | jq \
5266
+ '[.jobs[] | select(.name == "native deploy staging" and .conclusion == "success")] | length')"
5267
+ [ "$execute_count" = 1 ] || {
5268
+ echo 'Deploy must contain exactly one successful thin staging execution'
5269
+ exit 1
5270
+ }
5271
+ echo 'deployed=true' >> "$GITHUB_OUTPUT"
5214
5272
 
5215
5273
  plan:
5216
5274
  needs: deployment-classification
@@ -7093,7 +7151,7 @@ on:
7093
7151
  description: Protected stage
7094
7152
  required: true
7095
7153
  type: choice
7096
- options: [staging, production]
7154
+ options: [${protectedWorkflowStageChoices}]
7097
7155
  project:
7098
7156
  description: Exact Product Slice project
7099
7157
  required: true
@@ -7119,11 +7177,9 @@ jobs:
7119
7177
  contents: read
7120
7178
  id-token: write
7121
7179
  env:
7122
- AWS_DEPLOY_ROLE_ARN: \${{ secrets.AWS_DEPLOY_ROLE_ARN }}
7123
- AWS_EXPECTED_ACCOUNT_ID: \${{ vars.AWS_EXPECTED_ACCOUNT_ID }}
7124
- AWS_REGION: \${{ vars.AWS_REGION }}
7125
7180
  EBK_ACTION: \${{ inputs.action }}
7126
7181
  EBK_PROJECT: \${{ inputs.project }}
7182
+ EBK_RENDERED_PROFILE: ${deploymentProfile}
7127
7183
  EBK_STAGE: \${{ inputs.stage }}
7128
7184
  runs-on: ubuntu-latest
7129
7185
  steps:
@@ -7132,19 +7188,12 @@ jobs:
7132
7188
  EBK_PROTECTED_REF: \${{ github.ref }}
7133
7189
  run: |
7134
7190
  case "$EBK_STAGE:$EBK_PROTECTED_REF" in
7135
- staging:refs/heads/develop|production:refs/heads/main) ;;
7191
+ ${protectedWorkflowStageBranchCases}) ;;
7136
7192
  *) echo 'Secret metadata stage does not match the protected branch'; exit 1 ;;
7137
7193
  esac
7138
7194
  case "$EBK_PROJECT" in
7139
7195
  ''|-*|*-|*[!a-z0-9-]*) echo 'Secret metadata project must be an exact normalized Slice name'; exit 1 ;;
7140
7196
  esac
7141
- test -n "$AWS_DEPLOY_ROLE_ARN" || { echo "$EBK_STAGE AWS_DEPLOY_ROLE_ARN is required"; exit 1; }
7142
- test -n "$AWS_EXPECTED_ACCOUNT_ID" || { echo "$EBK_STAGE AWS_EXPECTED_ACCOUNT_ID is required"; exit 1; }
7143
- test -n "$AWS_REGION" || { echo "$EBK_STAGE AWS_REGION is required"; exit 1; }
7144
- case "$AWS_DEPLOY_ROLE_ARN" in
7145
- arn:aws:iam::$AWS_EXPECTED_ACCOUNT_ID:role/*) ;;
7146
- *) echo 'Secret metadata role does not belong to the expected account'; exit 1 ;;
7147
- esac
7148
7197
  - uses: ${checkoutAction}
7149
7198
  with:
7150
7199
  persist-credentials: false
@@ -7156,18 +7205,147 @@ jobs:
7156
7205
  - run: pnpm install --frozen-lockfile --ignore-scripts --ignore-pnpmfile
7157
7206
  - name: Verify managed workspace integrity before AWS authority
7158
7207
  run: pnpm nx run ${name}:workspace-health
7208
+ - id: policy
7209
+ name: Derive committed secret metadata stage policy
7210
+ run: |
7211
+ ${indentWorkflowScript(protectedSecretPolicyDerivationScript(), 10)}
7212
+ - id: authority
7213
+ name: Derive canonical committed stage authority
7214
+ env:
7215
+ EBK_DEVELOPMENT_ACCOUNT: \${{ steps.policy.outputs.development_account_id }}
7216
+ EBK_PRODUCTION_ACCOUNT: \${{ steps.policy.outputs.production_account_id }}
7217
+ EBK_REGION: \${{ steps.policy.outputs.deployment_region }}
7218
+ EBK_STAGING_ACCOUNT: \${{ steps.policy.outputs.staging_account_id }}
7219
+ run: |
7220
+ ${indentWorkflowScript(`set -euo pipefail\n${(0, workspace_files_thin_deploy_js_1.canonicalStageAuthorityDerivationScript)()}`, 10)}
7159
7221
  - uses: ${configureAwsAction}
7160
7222
  with:
7161
- role-to-assume: \${{ secrets.AWS_DEPLOY_ROLE_ARN }}
7223
+ audience: \${{ steps.authority.outputs.audience }}
7224
+ role-to-assume: \${{ steps.authority.outputs.role_arn }}
7162
7225
  role-session-name: ebk-secret-metadata-\${{ github.run_id }}-\${{ github.run_attempt }}
7163
- aws-region: \${{ vars.AWS_REGION }}
7164
- - name: Verify assumed account
7165
- run: test "$(aws sts get-caller-identity --query Account --output text)" = "$AWS_EXPECTED_ACCOUNT_ID"
7226
+ aws-region: \${{ steps.policy.outputs.deployment_region }}
7227
+ - name: Verify exact stage authority
7228
+ env:
7229
+ AWS_DEPLOY_ROLE_ARN: \${{ steps.authority.outputs.role_arn }}
7230
+ AWS_PARTITION: \${{ steps.authority.outputs.partition }}
7231
+ EBK_POLICY_ACCOUNT_ID: \${{ steps.authority.outputs.account_id }}
7232
+ run: |
7233
+ ${indentWorkflowScript((0, workspace_files_thin_deploy_js_1.exactStageDeployerGuardScript)('Secret metadata', '$EBK_POLICY_ACCOUNT_ID'), 10)}
7166
7234
  - name: Reconcile value-blind metadata
7235
+ env:
7236
+ AWS_DEPLOY_ROLE_ARN: \${{ steps.authority.outputs.role_arn }}
7237
+ AWS_EXPECTED_ACCOUNT_ID: \${{ steps.authority.outputs.account_id }}
7238
+ AWS_REGION: \${{ steps.policy.outputs.deployment_region }}
7167
7239
  run: |
7168
7240
  args=("--stage=$EBK_STAGE" "--region=$AWS_REGION")
7169
7241
  if [ "$EBK_ACTION" = apply ]; then args+=("--apply"); fi
7170
7242
  pnpm nx run "$EBK_PROJECT:secret-metadata" -- "\${args[@]}"
7243
+ `,
7244
+ '.github/workflows/secret-initialize.yml': `name: Secret initialize
7245
+
7246
+ on:
7247
+ workflow_dispatch:
7248
+ inputs:
7249
+ stage:
7250
+ description: Protected stage
7251
+ required: true
7252
+ type: choice
7253
+ options: [${protectedWorkflowStageChoices}]
7254
+ project:
7255
+ description: Exact Product Slice project
7256
+ required: true
7257
+ type: string
7258
+ logical_name:
7259
+ description: Exact machine-generated secret logical name
7260
+ required: true
7261
+ type: string
7262
+ action:
7263
+ description: Value-blind initialization action
7264
+ required: true
7265
+ default: report
7266
+ type: choice
7267
+ options: [report, apply]
7268
+
7269
+ concurrency:
7270
+ group: secret-initialize-\${{ inputs.stage }}-\${{ inputs.project }}-\${{ inputs.logical_name }}
7271
+ cancel-in-progress: false
7272
+
7273
+ permissions:
7274
+ contents: read
7275
+
7276
+ jobs:
7277
+ initialize:
7278
+ environment: \${{ inputs.stage }}
7279
+ permissions:
7280
+ contents: read
7281
+ id-token: write
7282
+ env:
7283
+ EBK_ACTION: \${{ inputs.action }}
7284
+ EBK_LOGICAL_NAME: \${{ inputs.logical_name }}
7285
+ EBK_PROJECT: \${{ inputs.project }}
7286
+ EBK_RENDERED_PROFILE: ${deploymentProfile}
7287
+ EBK_STAGE: \${{ inputs.stage }}
7288
+ runs-on: ubuntu-latest
7289
+ steps:
7290
+ - name: Validate protected request
7291
+ env:
7292
+ EBK_PROTECTED_REF: \${{ github.ref }}
7293
+ run: |
7294
+ case "$EBK_STAGE:$EBK_PROTECTED_REF" in
7295
+ ${protectedWorkflowStageBranchCases}) ;;
7296
+ *) echo 'Secret initialize stage does not match the protected branch'; exit 1 ;;
7297
+ esac
7298
+ case "$EBK_PROJECT" in
7299
+ ''|-*|*-|*[!a-z0-9-]*) echo 'Secret initialize project must be an exact normalized Slice name'; exit 1 ;;
7300
+ esac
7301
+ [[ "$EBK_LOGICAL_NAME" =~ ^[A-Za-z][A-Za-z0-9]{0,63}$ ]] ||
7302
+ { echo 'Secret initialize logical name is invalid'; exit 1; }
7303
+ - uses: ${checkoutAction}
7304
+ with:
7305
+ persist-credentials: false
7306
+ - uses: ${pnpmAction}
7307
+ - uses: ${setupNodeAction}
7308
+ with:
7309
+ node-version: ${tool_versions_js_1.WORKSPACE_NODE_VERSION}
7310
+ cache: pnpm
7311
+ - run: pnpm install --frozen-lockfile --ignore-scripts --ignore-pnpmfile
7312
+ - name: Verify managed workspace integrity before AWS authority
7313
+ run: pnpm nx run ${name}:workspace-health
7314
+ - id: policy
7315
+ name: Derive committed secret initialize stage policy
7316
+ run: |
7317
+ ${indentWorkflowScript(protectedSecretPolicyDerivationScript(), 10)}
7318
+ - id: authority
7319
+ name: Derive canonical committed stage authority
7320
+ env:
7321
+ EBK_DEVELOPMENT_ACCOUNT: \${{ steps.policy.outputs.development_account_id }}
7322
+ EBK_PRODUCTION_ACCOUNT: \${{ steps.policy.outputs.production_account_id }}
7323
+ EBK_REGION: \${{ steps.policy.outputs.deployment_region }}
7324
+ EBK_STAGING_ACCOUNT: \${{ steps.policy.outputs.staging_account_id }}
7325
+ run: |
7326
+ ${indentWorkflowScript(`set -euo pipefail\n${(0, workspace_files_thin_deploy_js_1.canonicalStageAuthorityDerivationScript)()}`, 10)}
7327
+ - uses: ${configureAwsAction}
7328
+ with:
7329
+ audience: \${{ steps.authority.outputs.audience }}
7330
+ role-to-assume: \${{ steps.authority.outputs.role_arn }}
7331
+ role-session-name: ebk-secret-initialize-\${{ github.run_id }}-\${{ github.run_attempt }}
7332
+ aws-region: \${{ steps.policy.outputs.deployment_region }}
7333
+ - name: Verify exact stage authority
7334
+ env:
7335
+ AWS_DEPLOY_ROLE_ARN: \${{ steps.authority.outputs.role_arn }}
7336
+ AWS_PARTITION: \${{ steps.authority.outputs.partition }}
7337
+ EBK_POLICY_ACCOUNT_ID: \${{ steps.authority.outputs.account_id }}
7338
+ run: |
7339
+ ${indentWorkflowScript((0, workspace_files_thin_deploy_js_1.exactStageDeployerGuardScript)('Secret initialize', '$EBK_POLICY_ACCOUNT_ID'), 10)}
7340
+ - name: Initialize machine-generated secret value-blind
7341
+ env:
7342
+ AWS_DEPLOY_ROLE_ARN: \${{ steps.authority.outputs.role_arn }}
7343
+ AWS_EXPECTED_ACCOUNT_ID: \${{ steps.authority.outputs.account_id }}
7344
+ AWS_REGION: \${{ steps.policy.outputs.deployment_region }}
7345
+ run: |
7346
+ args=("--logicalName=$EBK_LOGICAL_NAME" "--stage=$EBK_STAGE" "--region=$AWS_REGION")
7347
+ if [ "$EBK_ACTION" = apply ]; then args+=("--apply"); fi
7348
+ pnpm nx run "$EBK_PROJECT:secret-initialize" -- "\${args[@]}"
7171
7349
  `,
7172
7350
  '.github/workflows/sync-back.yml': `name: Sync protected main history
7173
7351
 
@@ -7457,9 +7635,10 @@ infrastructure or protected deployment policy. Preserve the ownership boundary r
7457
7635
  \`.ebk/ownership.json\`: managed files belong to EBK, seeded files are generated starting
7458
7636
  points that adopters may change, and unrecorded files are adopter-owned.
7459
7637
 
7460
- Secret values are a human-only TTY ceremony. Agents and CI must never invoke
7461
- \`ebk secret set\` or \`ebk secret remove\`; they may use only value-blind
7462
- metadata, status, and verification commands.
7638
+ Human-supplied secret values are a human-only TTY ceremony. Agents and CI must
7639
+ never invoke \`ebk secret set\` or \`ebk secret remove\`. They may use
7640
+ value-blind metadata, status, verification, and the exact generated-secret
7641
+ initializer, which accepts and returns no plaintext value.
7463
7642
 
7464
7643
  ## Work markers
7465
7644
 
@@ -7519,50 +7698,6 @@ export default tseslint.config(
7519
7698
  rules: { 'no-console': 'error' },
7520
7699
  },
7521
7700
  );
7522
- `,
7523
- 'README.md': `# ${name}
7524
-
7525
- This workspace was created with Empire Builder Kit v1.
7526
-
7527
- ## Start here
7528
-
7529
- 1. Review \`docs/architecture.md\`.
7530
- 2. Review the generated Foundation before deploying it.
7531
- 3. Add product slices through the Empire Builder Kit Nx plugin.
7532
- 4. Review \`docs/repository-operating-policy.md\` and \`docs/git-workflow.md\` before enabling production.
7533
- 5. Read \`AGENTS.md\` and \`docs/work-markers.md\` before assigning generated customization work to agents.
7534
-
7535
- For one-origin fleet development, initialize the workspace-pinned CLI once with
7536
- \`ebk dev configure --profile=<bounded-development-profile>\`, inspect the certificate reported
7537
- by \`ebk dev setup\`, and then explicitly approve \`ebk dev setup --trust\`. The supported v1
7538
- origin uses the complete explicit-port HTTPS URL reported at startup; optional
7539
- \`--portless\` setup is experimental. \`ebk dev doctor\` requires a real exact-host TLS
7540
- handshake for trust and connectivity readiness, and reports portless access ready
7541
- only after a live port-443 probe. Complete
7542
- \`aws sso login --profile <bounded-development-profile>\`, then start selected Slices with
7543
- \`ebk dev up billing accounts\`. The CLI derives the personal stage from the operating-system
7544
- account, reads the saved profile and Region, propagates that AWS context to child Slices, and
7545
- uses SST \`mono\` mode so agents and non-cursor-addressable terminals receive ordinary logs.
7546
- Use \`ebk dev status\`, \`ebk dev logs\`, \`ebk dev tail\`, \`ebk dev restart\`,
7547
- \`ebk dev down\`, and \`ebk dev doctor\` without supplying a stage. Setup, doctor, and the
7548
- managed root Fleet target remain internal Nx-backed implementation surfaces; \`ebk dev\` is the
7549
- framework-local development interface. For the development namespace, if the shell does not
7550
- expose workspace binaries, \`pnpm exec ebk dev ...\` only locates the workspace-pinned binary;
7551
- it is not another framework command surface. Protected Blueprint receipt review must instead
7552
- use the direct installed \`.bin/ebk blueprint verification-review\` entry documented in
7553
- \`docs/blueprints.md\`.
7554
-
7555
- Workspace creation only writes source files. It does not create or change cloud resources.
7556
-
7557
- New workspaces default to the one-account \`starter\` deployment profile. Configure the exact
7558
- shared account and Region in \`ebk.repository-policy.json\`, authenticate through IAM Identity
7559
- Center, add exact development and staging CIDRs to
7560
- \`packages/foundation/security/router-waf.json\`, and use
7561
- \`pnpm nx run ${name}:deploy-local -- --stage=development
7562
- --profile=<profile> --expectedRoleArn=<role-arn>\`. Never invoke SST directly.
7563
- For a brand-new stage with generated Product Slices, complete the documented workload-role
7564
- Foundation publication and pass \`--phase=foundation-bootstrap\` once before publishing and
7565
- deploying the complete stage.
7566
7701
  `,
7567
7702
  'docs/architecture.md': `# Workspace architecture
7568
7703
 
@@ -7576,347 +7711,15 @@ The Standard Foundation lives at \`packages/foundation\`. Product slices live be
7576
7711
  \`packages/<slice-name>\` and consume versioned Foundation contracts instead of importing
7577
7712
  Foundation implementation files.
7578
7713
 
7579
- Canonical consumer stages use the matching \`development\`, \`staging\`, or \`production\`
7580
- Foundation. Framework-derived personal stages may use only the \`development\`
7581
- Foundation; generic preview stages are unsupported.
7714
+ This workspace's \`${deploymentProfile}\` profile activates only:
7715
+ ${activeStages.join(', ')}. Each active canonical consumer stage uses the matching Foundation.
7716
+ Inactive canonical stages are rejected. Framework-derived personal stages may use only the
7717
+ \`development\` Foundation; generic preview stages are unsupported.
7582
7718
 
7583
- The committed deployment profile changes account and local/GitHub authority only. Every
7584
- canonical stage always owns a separate Router/domain, Aurora cluster and migration history,
7719
+ Every active canonical stage owns a separate Router/domain, Aurora cluster and migration history,
7585
7720
  Cognito user pool, event bus, secret namespace, IAM boundary, logs, queues, ECR namespace, and
7586
- stage-qualified SSM discovery contract. Discovery rejects a record for another canonical stage.
7587
- `,
7588
- 'docs/git-workflow.md': `# Protected Git workflow
7589
-
7590
- Ordinary work starts from \`develop\` and returns through a reviewed pull request to
7591
- \`develop\`. A production promotion is exactly \`develop\` to \`main\`. After production,
7592
- review and merge the managed \`main\` to \`develop\` sync-back pull request before ordinary
7593
- work resumes.
7594
-
7595
- Both protected branches require the stable \`policy\` and \`verify\` checks, one approving
7596
- review including an approval from the committed catch-all CODEOWNER and someone other than the
7597
- most recent pusher, dismissed stale reviews, resolved conversations, and merge commits. Replace
7598
- the placeholder in \`.github/CODEOWNERS\` with a GitHub user or team that has repository access
7599
- before bootstrap. An approval from \`github-actions[bot]\` cannot satisfy the independent
7600
- code-owner gate; the Actions repository setting is needed to create the managed sync-back pull
7601
- request, not to approve it.
7602
- Direct updates, force pushes, deletion, squash merging, and rebase merging are prohibited.
7603
- GitHub rulesets, environments, and AWS IAM are the authorization boundaries; local hooks are
7604
- only defense in depth.
7605
-
7606
- The \`staging\` environment accepts only \`develop\`; \`production\` accepts only \`main\`.
7607
- These workflows exist for every deployment profile. The staging workflow is optional for
7608
- \`starter\`, whose governed local Nx target may deploy development and staging. Production is
7609
- GitHub-only for every profile; \`fully-isolated\` also requires GitHub for staging.
7610
- The committed \`productionApproval.mode\` selects one of two production authorization
7611
- contracts:
7612
-
7613
- - \`promotion-pr\` is compatible with private repositories on GitHub Team. The required
7614
- non-author CODEOWNER approval on the exact \`develop\` to \`main\` promotion pull request
7615
- is the production authorization. Merging that pull request starts production deployment;
7616
- there is no second EBK-managed environment approval or wait timer. The approver may also be
7617
- the human whose merge triggers deployment. Roll back through a reviewed revert on the
7618
- protected branches and let the resulting \`main\` push redeploy. A repository write actor who
7619
- can run Actions may also manually dispatch \`deploy\`, \`refresh\`, or \`unlock\` against the
7620
- already approved \`main\` tip without another human gate. That residual cannot introduce
7621
- unreviewed source; production \`remove\` remains separately variable-gated. Restrict write and
7622
- Actions access, audit dispatches, or select \`environment-reviewer\` when every production
7623
- action needs separate approval.
7624
- - \`environment-reviewer\` adds a separate production environment reviewer with self-review
7625
- prevention. A private repository needs GitHub Enterprise capability for this mode. Bootstrap
7626
- inspects that capability and fails closed on an unsupported plan; it never silently changes
7627
- the committed mode.
7628
-
7629
- Both modes retain required checks, a non-author CODEOWNER approval, no ruleset bypass, the
7630
- literal production environment, and its branch-scoped credentials. The base \`staging\` and
7631
- \`production\` environments own deployment, smoke, and ECR evidence roles plus their
7632
- variables; isolated profiles also keep the staging-registry promotion pull role in production.
7633
- Starter promotion authenticates once to the shared account and copies the exact staging digest
7634
- into a distinct production ECR repository. Dedicated
7635
- \`infrastructure-plan-<stage>\` environments gate read-only role use. Separate
7636
- \`async-recovery-<stage>\`, \`performance-aggregate-<stage>\`,
7637
- \`performance-cost-<stage>\`, \`performance-load-<stage>\`, and
7638
- \`reliability-read-<stage>\` environments own only the role and variables required by that
7639
- capability. Infrastructure-plan jobs use a repository-variable read-only role/input inventory;
7640
- an independent clean audit job assumes that exact environment-subject role and verifies its
7641
- trust and complete inline policy before any repository-backed plan job may assume it. Plan-role
7642
- trust uses GitHub's immutable repository identity subject,
7643
- \`repo:<owner>@<owner-id>/<repository>@<repository-id>:environment:<environment>\`.
7644
- The audit derives it from the workflow's repository name, owner ID, repository ID, and literal
7645
- environment and refuses missing IDs or the older mutable name-only subject. Update every
7646
- adopter-owned AWS OIDC role trust to the immutable identity before protected use; never retain
7647
- both forms. Every
7648
- \`*-staging\` environment accepts only \`develop\`; every
7649
- \`*-production\` environment accepts only \`main\`; reviewer, self-review, and wait protection
7650
- matches the corresponding base stage. The credential-free \`npm-prerelease\` environment
7651
- accepts only \`main\` and contains no secrets or variables. In Team-compatible
7652
- \`promotion-pr\` mode, protected-main review plus the exact current-main tag binding is
7653
- publication authorization, so it has no reviewer or wait rule. In
7654
- \`environment-reviewer\` mode it mirrors production protection. Never add repository- or
7655
- organization-visible fallbacks for protected values. Keep repository default workflow
7656
- permissions read-only; generated jobs request only the additional permissions they need.
7657
-
7658
- \`EBK_FLEET_ORIGIN\` is the sole protected deployment and smoke origin. Read-only plan jobs
7659
- enter only their dedicated \`infrastructure-plan-<stage>\` environment; their stage-named
7660
- repository variables \`EBK_STAGING_FLEET_ORIGIN\` and
7661
- \`EBK_PRODUCTION_FLEET_ORIGIN\` remain plan inputs only. Before mutation, each
7662
- base-environment-protected deployment requires
7663
- its \`EBK_FLEET_ORIGIN\` to equal the origin retained by the plan.
7664
-
7665
- \`AWS_ASYNC_RECOVERY_ROLE_ARN\` is a separate mutation role. Trust only this
7666
- repository's exact \`async-recovery-<stage>\` GitHub OIDC subject and grant only
7667
- the recovery operations/resources declared by generated capability adapters.
7668
-
7669
- \`AWS_PERFORMANCE_LOAD_ROLE_ARN\` is a separate read-only OIDC role. Trust only
7670
- this repository's exact \`performance-load-<stage>\` GitHub OIDC subject.
7671
- Set that environment's \`EBK_PERFORMANCE_ORIGINS_JSON\` to a JSON object that
7672
- maps every reviewed Slice to the exact \`performanceEndpoint\` emitted by its
7673
- current deployment; never copy the map to repository scope or substitute the
7674
- fleet web origin. The workflow selects only its requested Slice, and the
7675
- committed load plan must name that same origin and exact reserved Slice path.
7676
- Grant \`sts:GetCallerIdentity\` and \`secretsmanager:GetSecretValue\` only on
7677
- \`arn:<partition>:secretsmanager:<region>:<account>:secret:ebk/<stage>/<slice>/*\` for
7678
- reviewed Slices. Do not grant secret writes, deployment, CloudFormation, SST,
7679
- or CloudWatch Logs access. The protected workflow never logs or uploads HMAC
7680
- material and removes its new job-scoped 0600 file after cleanup.
7681
- Bootstrap verifies environment-only secret placement, not the role's hidden
7682
- AWS trust or permission policy.
7683
-
7684
- \`AWS_PERFORMANCE_AGGREGATE_ROLE_ARN\` is a separate adopter-owned read-only
7685
- OIDC role. Trust only this repository's exact
7686
- \`performance-aggregate-<stage>\` GitHub OIDC subject. Grant
7687
- \`sts:GetCallerIdentity\` plus only the exact data-read actions and resource
7688
- ARNs required by the adopted \`performance/aggregate-count-provider.ts\`.
7689
- Do not grant writes, deployment, CloudFormation, SST, IAM, Cost Explorer, or
7690
- unrelated data access. The generated workflow validates the protected Deploy
7691
- artifact before role assumption, verifies the STS account and environment
7692
- Region, withholds the OIDC bearer, and clears the AWS session before artifact
7693
- retention. Bootstrap verifies environment-only secret placement, not the
7694
- role's hidden AWS trust or least-privilege permission policy.
7695
-
7696
- \`AWS_PERFORMANCE_COST_ROLE_ARN\` is a separate read-only OIDC role. Trust only
7697
- this repository's exact \`performance-cost-<stage>\` GitHub OIDC subject.
7698
- Grant only \`sts:GetCallerIdentity\`, \`ce:ListCostAllocationTags\`, and
7699
- \`ce:GetCostAndUsage\`; Cost Explorer
7700
- queries are account-global, while the retained evidence still binds the
7701
- selected resource Region. Do not grant budget, anomaly-monitor, tag, billing,
7702
- deployment, CloudFormation, SST, or other mutation authority. Bootstrap verifies
7703
- environment-only secret placement, not the role's hidden AWS trust or
7704
- permission policy.
7705
-
7706
- The framework manages
7707
- \`.github/workflows/performance-aggregate-counts.yml\`, its exact Deploy
7708
- binding, evidence wrapper, artifact name, and canonical schema. The seeded
7709
- \`performance/aggregate-count-provider.ts\` remains adopter-owned and fails
7710
- closed until it returns only the reviewed business counts and provider request
7711
- reference. The provider runs only with the dedicated aggregate-read role after
7712
- the framework validates the exact protected release and STS account. The
7713
- workflow binds those untrusted counts to the environment-owned account/Region,
7714
- closed period, exact current protected release, Slice, stage, and workspace
7715
- and records the dedicated OIDC access mode and adopter-owned least-privilege
7716
- read contract before it emits the single
7717
- \`aggregate-count-<stage>-<slice>-<run>-<attempt>\` artifact. The framework
7718
- does not invent counts or copy them from request-level dimensions.
7719
-
7720
- \`performance-cost-actual.yml\` treats supplied run IDs and artifact names only
7721
- as selectors. Before OIDC it queries GitHub for the fixed aggregate workflow
7722
- and generated \`Deploy\` workflow runs, requiring the same repository,
7723
- candidate, protected branch, successful conclusion, allowed event, exact
7724
- workflow name/path, and one exact nonexpired artifact. It derives the compact
7725
- performance release binding from that Deploy artifact and separately verifies
7726
- the independently approved aggregate-count and release-binding byte digests.
7727
-
7728
- \`AWS_RELIABILITY_READ_ROLE_ARN\` is a separate read-only OIDC role. Trust only
7729
- this repository's exact \`reliability-read-<stage>\` GitHub OIDC subject.
7730
- Grant \`sts:GetCallerIdentity\`, \`cloudwatch:DescribeAlarms\`,
7731
- \`cloudwatch:DescribeAlarmHistory\`, \`cloudwatch:GetMetricData\`,
7732
- \`ecs:DescribeServices\`, and \`states:DescribeExecution\`. Scope ECS and Step
7733
- Functions resources to the reviewed Slice services, state machines, and
7734
- executions wherever IAM supports resource-level constraints. Do not grant log
7735
- reads, deployment, CloudFormation, SST, or any AWS mutation.
7736
-
7737
- The generated \`reliability-read.yml\` workflow selects the newest nonexpired
7738
- binding artifact from a successful deployed-smoke run, reconciles it to the
7739
- current successful protected deployment, and checks out that exact commit
7740
- before assuming the role. Callers choose only a bounded operation and workload;
7741
- they cannot supply the AWS account, Region, role, or binding path. The binding
7742
- allowlists exact alarm names, API ID, ECS services, and Step Functions
7743
- resources before the adapter makes a service call. Bootstrap verifies
7744
- environment-only secret placement, not the role's hidden AWS trust or
7745
- permission policy.
7746
-
7747
- The async-recovery, performance-load, performance-cost, and reliability-read
7748
- jobs have job-scoped \`id-token: write\`. They install the frozen dependency
7749
- graph with lifecycle scripts and pnpm hooks disabled before assuming AWS
7750
- authority. Immediately after the one pinned credential action, they blank the
7751
- OIDC request URL and bearer for future steps and explicitly shadow both values
7752
- empty on every later step. After bounded AWS use, they blank current AWS
7753
- credential variables before evidence upload. This withholds credentials from
7754
- later processes; it does not claim that GitHub revokes the job-level
7755
- permission.
7756
-
7757
- \`AWS_SMOKE_ROLE_ARN\` is separate from deployment authority. Trust only this
7758
- repository's GitHub OIDC subject for the matching protected environment. Grant
7759
- only run-scoped Cognito principal setup/cleanup on reviewed user-pool ARNs and
7760
- \`logs:FilterLogEvents\` on exact
7761
- \`/ebk/<stage>/<slice>/event-consumer\` log-group ARNs. Do not grant
7762
- CloudFormation, SST mutation, broad Logs query, \`GetLogEvents\`,
7763
- \`StartQuery\`, or \`logs:Unmask\`. Bootstrap verifies environment-only secret
7764
- inventory but never creates or reads the secret, materializes the role, or
7765
- attests its hidden policy.
7766
-
7767
- Run the GitHub bootstrap in report mode first. Source upgrades never alter remote GitHub
7768
- settings. Apply a reviewed report only after \`develop\` exists and the candidate's exact
7769
- \`policy\` and \`verify\` check contexts have run.
7770
-
7771
- Set \`GH_REPO=<owner>/<repository>\` and a suitable \`GH_TOKEN\`, then run
7772
- \`pnpm nx run ${name}:github-bootstrap\`. Review the deterministic report before rerunning
7773
- with \`--apply\`. The command never creates branches, credentials, secrets, or AWS resources.
7774
- Supply \`--npmPrereleaseReviewerIds=<id,...>\` only in
7775
- \`environment-reviewer\` mode when intentionally setting a distinct npm
7776
- mutation reviewer set.
7777
- Supply \`--productionReviewerIds=<id,...>\` only when committed policy selects
7778
- \`environment-reviewer\`; it is rejected in \`promotion-pr\` mode.
7779
-
7780
- Install the optional defense-in-depth pre-push hook explicitly with
7781
- \`pnpm nx run ${name}:install-git-hooks\`. It refuses to overwrite an unrelated existing hook.
7782
-
7783
- The sync-back workflow uses only \`GITHUB_TOKEN\` and never pushes or auto-merges. GitHub may
7784
- place its pull-request workflows in an approval-required state. Approve them; if no run
7785
- materializes, close and reopen the pull request as a human and then approve it.
7786
- `,
7787
- 'docs/repository-operating-policy.md': `# Repository Operating Policy
7788
-
7789
- \`ebk.repository-policy.json\` is the reviewed source of truth for deployment profile, exact
7790
- stage account IDs, deployment Region, production access, production approval, per-Slice
7791
- readiness, and the one shared production Foundation profile.
7792
- New workspaces start with production disabled and explicit Team-compatible
7793
- \`productionApproval.mode: "promotion-pr"\`. A missing approval mode or Slice entry fails
7794
- closed; no GitHub capability is allowed to choose a mode implicitly.
7795
-
7796
- Deployment profiles are proportional to operator maturity:
7797
-
7798
- - \`starter\` (default) requires one shared account, permits local development and staging
7799
- through the governed root Nx target, and reserves production for protected GitHub deployment;
7800
- - \`production-isolated\` requires one development/staging account and a distinct production
7801
- account; only production is GitHub-only; and
7802
- - \`fully-isolated\` requires three distinct accounts; staging and production are GitHub-only.
7803
-
7804
- Equal account IDs never imply shared stage resources. Router, Aurora, Cognito, events, secrets,
7805
- IAM, logs, queues, resource names, and SSM discovery remain stage-specific. Existing prerelease
7806
- repositories migrate explicitly to \`fully-isolated\`; upgrades never infer or weaken topology.
7807
- That profile-only migration retains the former authority model but stays undeployable until exact
7808
- development, staging, and production account IDs plus \`deploymentRegion\` are committed.
7809
-
7810
- The named repository modes are source-writing presets, not runtime state. Report a change
7811
- first and inspect the complete current/next policy:
7812
-
7813
- \`\`\`bash
7814
- pnpm nx g @empire-builder-kit/nx:repository-policy \\
7815
- --deploymentProfile=starter --deploymentRegion=us-east-1 \\
7816
- --developmentAccountId=111122223333 --stagingAccountId=111122223333 \\
7817
- --productionAccountId=111122223333
7818
- \`\`\`
7819
-
7820
- Apply the reviewed source change separately with \`--apply\`. This command never contacts AWS
7821
- or GitHub. Use \`--status=blocked|pilot|ready\` for explicit Slice transitions and
7822
- \`--profile=cost-optimized|standard\` for an explicit shared Foundation decision.
7823
- Use \`--productionApprovalMode=promotion-pr|environment-reviewer\` for an explicit approval
7824
- transition. This changes committed policy only; a separate bootstrap report/apply reconciles
7825
- remote GitHub settings.
7826
-
7827
- After applying exact topology, authenticate with temporary federated credentials for development
7828
- or staging and invoke
7829
- \`pnpm nx run ${name}:deploy-local -- --stage=<stage> --profile=<profile>
7830
- --expectedRoleArn=<role-arn>\`. Production runs only through the protected main-branch GitHub
7831
- workflow. Each admitted local invocation plans the complete current stage, displays the stage
7832
- summary and first material project, and requires a real TTY response of
7833
- \`approve:<action>:<stage>:<project>:<current-plan-digest>\`. A human or authorized agent may type
7834
- that acknowledgement; it is active operator control, not a claim of independent human review.
7835
- The target creates one private ten-minute capability for that project, rechecks the exact source,
7836
- artifacts, account, Region, role, stage, policy, state, and lock without a second provider preview,
7837
- consumes the capability once, and removes it on every exit path. Unattended scripts cannot approve.
7838
- Every material deploy or refresh mutates at most the first material project and ends with a
7839
- controlled non-success \`replan-required\` handoff, including the last project in the ordered stage.
7840
- Rerun the complete stage, review the new current summary, and repeat until one invocation proves
7841
- every project is semantically converged. Only that convergence invocation succeeds. A preview is
7842
- recent review and audit evidence; it is not represented as a cryptographically replayable Pulumi
7843
- transaction. A complete, unlocked deploy plan containing only provider reads with no changed
7844
- properties, replacement, failure, or unmatched risk finding is retained as observation evidence
7845
- and counts as semantically converged. The standalone project planner may still return its
7846
- conservative non-success for that drift observation; the root local transaction accepts only a
7847
- freshly emitted exact provider-read-only record from that child. Repeated reads coalesce
7848
- only when their logical ID and resource type match exactly. Any stale or ambiguous
7849
- observation fails closed; a plan with desired changes remains material and requires review. One
7850
- narrower case stays reviewable instead of becoming
7851
- drift: when an upstream Foundation apply changes only both external framework-version tag
7852
- projections (plus SST's already-bounded name hydration), the retained
7853
- external identity must match one exact non-replacing provider read in the same ordinary plan.
7854
- Missing read correlation, owned resources, other tag or property changes, incomplete tag pairs,
7855
- and provider failures remain drift and fail closed. The local target requires the exact SST v5 account bootstrap normally
7856
- created by the pinned \`ebk dev up\` path, then creates and attests only missing exact
7857
- per-project/canonical-stage state passphrases before its formal read-only plans. This passphrase
7858
- bootstrap is the only persistent pre-acknowledgement write; it creates no deployment authority
7859
- and is reported in bounded local evidence. Clean immutable source, immediate per-project plans,
7860
- migration and retention preflights, and secret-free result
7861
- evidence remain mandatory. Direct SST is forbidden.
7862
-
7863
- After an interrupted apply, use the same target with \`--action=refresh\` and the exact
7864
- phase, stage, profile, and role. It retains the same plan-summary review boundary, admits only
7865
- bounded drift-detected refresh evidence, and never turns refresh into an implicit deploy. When
7866
- the interrupted write leaves a recorded provider output unusable by the ordinary desired preview,
7867
- the plan stops without mutation. The non-writing refresh preview separates provider refresh
7868
- records from its later desired preview and cannot replace failed ordinary desired authority;
7869
- repair the recorded dependency or use the documented
7870
- operator recovery path before planning again.
7871
-
7872
- A stage-wide refresh still plans and authorizes every selected project. When one exact retained
7873
- project plan proves an absent SST state that is unlocked, complete, empty, and drift-free, the
7874
- root target records that project as a refresh no-op instead of invoking SST against a nonexistent
7875
- stage. It also retains the pinned provider's exact SSM \`ebk:framework-version\` \`tagsAll\`
7876
- normalization as a semantically inert observation after the same bounded shape
7877
- repeats following refresh. Value changes, other tags, security findings, deletes, replacements,
7878
- sensitive properties, and every other resource type remain material. Locked, incomplete, failed,
7879
- or otherwise drifted plans are never skipped.
7880
-
7881
- For a stage with no workload-role account baseline, publish the fixed Foundation-only candidate
7882
- and invoke this root target with \`--phase=foundation-bootstrap\`. After Foundation outputs exist,
7883
- publish the complete \`full-stage\` candidate and invoke the target without \`--phase\` (or with
7884
- \`--phase=full-stage\`). The bootstrap phase selects exactly Foundation; it is not an arbitrary
7885
- project filter.
7886
-
7887
- Validation requires both an explicit production approval mode and an explicit Foundation
7888
- profile. It does not infer omitted values. Use \`promotion-pr\` for a private GitHub Team
7889
- repository. Use \`environment-reviewer\` only when the repository plan can enforce a separate
7890
- environment reviewer.
7891
-
7892
- - \`blocked\` prohibits production create, update, and migration.
7893
- - \`pilot\` permits protected evidence gathering without claiming normal latency or maturity.
7894
- - \`ready\` records an adopter decision after its evidence and review; it is not inferred.
7895
- - removal and unlock remain explicit protected recovery paths so disabled policy cannot strand
7896
- resources.
7897
-
7898
- Protected production deploy and refresh validate the complete Foundation/Slice set before AWS
7899
- access. The Foundation is always ordered first. The set is derived from committed policy and
7900
- cannot be narrowed with an ad hoc project argument. The retained stage preview remains review
7901
- evidence. Immediately before each project mutation the workflow computes one fresh plan against
7902
- current state and applies it without a second redundant preview. Source, artifacts, configuration,
7903
- policy, environment, and protected identities remain exact. Provider-only read/discard observations
7904
- may appear or disappear, but any new delete, replacement, import, stateful or security change, IAM
7905
- expansion, protection reduction, or unclassified resource stops for renewed approval. After an
7906
- upstream mutation, every downstream project therefore receives a fresh independent risk decision.
7907
-
7908
- \`standard\` keeps Aurora warm at 2–16 ACU. \`cost-optimized\` keeps a smaller
7909
- 0.5–4 ACU warm floor for starter production traffic. Both keep managed NAT,
7910
- 30-day backups, deletion protection, retained data, and the protected GitHub/OIDC/AWS path.
7911
- Changing policy affects only later lifecycle requests; it never deploys or removes resources.
7912
- Changing profile requires a separate reviewed Foundation deployment and retained operational
7913
- evidence.
7914
-
7915
- \`EBK_PRODUCTION_FOUNDATION_PROFILE\` is reserved managed executor input. The lifecycle
7916
- executor derives it from validated committed policy immediately before SST and removes any
7917
- ambient shell value. Do not set it as a local, repository, organization, or environment
7918
- variable. Incubation keeps the last configured profile so protected remove and unlock can still
7919
- render existing production resources; production deploy remains disabled.
7721
+ stage-qualified SSM discovery contract. Discovery rejects a record for an inactive or different
7722
+ canonical stage.
7920
7723
  `,
7921
7724
  'docs/work-markers.md': `# Agent work markers
7922
7725
 
@@ -8135,8 +7938,18 @@ must be committed, reviewable, and auditable.
8135
7938
  executor: 'nx:run-commands',
8136
7939
  options: { command: 'node tools/install-ebk-hooks.mjs' },
8137
7940
  },
7941
+ 'stage-authority-render': {
7942
+ cache: false,
7943
+ executor: 'nx:run-commands',
7944
+ outputs: ['{workspaceRoot}/.ebk/authority/{args.stage}/*'],
7945
+ options: {
7946
+ command: 'node tools/stage-authority-render.mjs',
7947
+ forwardAllArgs: true,
7948
+ },
7949
+ },
8138
7950
  'deploy-local': {
8139
7951
  cache: false,
7952
+ dependsOn: ['workspace-health'],
8140
7953
  executor: '@empire-builder-kit/nx:local-deploy',
8141
7954
  options: {
8142
7955
  stage: 'development',
@@ -8165,19 +7978,6 @@ must be committed, reviewable, and auditable.
8165
7978
  executor: '@empire-builder-kit/nx:work-markers',
8166
7979
  options: {},
8167
7980
  },
8168
- 'workload-role-candidate-report': {
8169
- executor: 'nx:run-commands',
8170
- options: {
8171
- command: 'node tools/workload-role-baseline/report-candidate-bundle.mjs',
8172
- forwardAllArgs: true,
8173
- },
8174
- },
8175
- 'workload-role-provisioner-build': {
8176
- executor: 'nx:run-commands',
8177
- options: {
8178
- command: 'node tools/workload-role-baseline/build.mjs',
8179
- },
8180
- },
8181
7981
  verify: {
8182
7982
  dependsOn: ['blueprint-trust-check', 'supply-chain-check'],
8183
7983
  executor: 'nx:run-commands',
@@ -8194,27 +7994,6 @@ must be committed, reviewable, and auditable.
8194
7994
  },
8195
7995
  },
8196
7996
  }),
8197
- 'packages/foundation/README.md': `# Standard Foundation
8198
-
8199
- This project owns the workspace-wide AWS platform for each canonical \`development\`,
8200
- \`staging\`, or \`production\` stage. It is a separate SST state boundary from product slices.
8201
- Never deploy it at a framework-derived personal stage; that consumer maps to
8202
- \`development\`.
8203
-
8204
- It provides the v1 network, routing, container compute, Postgres, identity, events,
8205
- observability, configuration, and deployment capabilities. Product slices consume its
8206
- versioned contract; they do not import files from \`infra/\`.
8207
-
8208
- Workspace creation does not deploy this project. Review \`docs/costs.md\`,
8209
- \`docs/security.md\`, and \`docs/runbooks.md\` before the first deployment.
8210
-
8211
- Set \`EBK_ROOT_DOMAIN\` before deploying when Route 53 in the target account owns a public
8212
- domain. Without it, the shared Router uses its functional provider URL.
8213
-
8214
- Before deploying development or staging, add at least one exact operator/probe CIDR to
8215
- \`security/router-waf.json\`. Production is public by default behind the same managed and
8216
- rate-limiting baseline; select its allowlist mode explicitly when required.
8217
- `,
8218
7997
  'packages/foundation/contracts/foundation.json': json({
8219
7998
  contractVersion: '1.0.0',
8220
7999
  capabilities: [
@@ -8243,11 +8022,15 @@ rate-limiting baseline; select its allowlist mode explicitly when required.
8243
8022
  dataRetention: 'retained',
8244
8023
  nat: 'ec2',
8245
8024
  },
8246
- staging: {
8247
- aurora: 'scale-to-zero',
8248
- dataRetention: 'retained',
8249
- nat: 'ec2',
8250
- },
8025
+ ...(deploymentProfile === 'starter'
8026
+ ? {}
8027
+ : {
8028
+ staging: {
8029
+ aurora: 'scale-to-zero',
8030
+ dataRetention: 'retained',
8031
+ nat: 'ec2',
8032
+ },
8033
+ }),
8251
8034
  production: {
8252
8035
  aurora: 'repository-policy',
8253
8036
  dataRetention: 'retained-and-protected',
@@ -8271,8 +8054,7 @@ The machine-readable stage inventory is \`../contracts/costs.json\`; keep this
8271
8054
  seeded decision record aligned with organization-specific budgets and alerts.
8272
8055
 
8273
8056
  - Development uses EC2 NAT, a development bastion, and Aurora scale-to-zero.
8274
- - Staging uses EC2 NAT and Aurora scale-to-zero by default.
8275
- - Production always uses managed NAT and retains protected data. Its explicit Repository
8057
+ ${deploymentProfile === 'starter' ? '' : '- Staging uses EC2 NAT and Aurora scale-to-zero by default.\n'}- Production always uses managed NAT and retains protected data. Its explicit Repository
8276
8058
  Operating Policy profile selects either the standard 2-ACU Aurora floor or the
8277
8059
  cost-optimized 0.5-ACU warm floor.
8278
8060
  - ECS, EventBridge, Cognito, SNS, SSM, and CloudWatch are primarily usage-priced; logs,
@@ -8280,102 +8062,6 @@ seeded decision record aligned with organization-specific budgets and alerts.
8280
8062
 
8281
8063
  Run \`foundation:remove\` only for disposable stages. Production is retained and protected by
8282
8064
  the SST configuration.
8283
- `,
8284
- 'packages/foundation/docs/runbooks.md': `# Foundation runbooks
8285
-
8286
- ## Deployment
8287
-
8288
- Use Nx for every supported lifecycle action:
8289
-
8290
- - \`pnpm nx run foundation:deploy --stage=development\`
8291
- - \`pnpm nx run foundation:refresh --stage=development\`
8292
- - \`pnpm nx run foundation:unlock --stage=development\`
8293
- - \`pnpm nx run foundation:remove --stage=development\`
8294
-
8295
- Do not invoke SST lifecycle commands directly. The root \`deploy-local\` target is the governed
8296
- local path for stages admitted by the committed deployment profile. Its fixed
8297
- \`foundation-bootstrap\` phase is the only local partial-stage path and exists solely for the
8298
- documented first workload-role publication; isolated protected stages remain on the trusted
8299
- GitHub path.
8300
-
8301
- Every local invocation plans the complete selected stage but mutates at most one material
8302
- project. Type the exact current \`approve:<action>:<stage>:<project>:<plan-digest>\` challenge in a
8303
- real TTY after reviewing the displayed summary. A human or authorized agent may operate that TTY;
8304
- unattended input is refused. The acknowledgement creates one ten-minute, one-use, private
8305
- capability bound to source, artifacts, policy, environment, credentials, and current state. A
8306
- completed deploy or refresh returns the controlled non-success \`replan-required\`; rerun the
8307
- complete stage, review the new current summary, and continue one project at a time. Only a
8308
- semantically converged complete stage reports success. No upstream mutation can authorize a
8309
- downstream project. A complete, unlocked deploy plan containing only provider reads with no
8310
- changed properties, replacement, failure, or unmatched risk finding is retained as observation
8311
- evidence and counts as semantically converged. The standalone project planner may still return
8312
- its conservative non-success for that drift observation; the root local transaction accepts only
8313
- a freshly emitted exact provider-read-only record from that child. Repeated reads coalesce
8314
- only when their logical ID and resource type match exactly. Any stale or ambiguous
8315
- observation fails closed; a plan with desired changes remains material and requires review. A
8316
- material downstream plan may also retain an upstream Foundation framework-version tag advance as
8317
- observation only when both external tag
8318
- projections, the retained identity, optional bounded SST name hydration, and one non-replacing
8319
- provider read correlate exactly. Every other external property or uncorroborated read remains
8320
- drift and blocks before the TTY challenge.
8321
-
8322
- For an interrupted local apply, run the same root target with \`--action=refresh\`, review its
8323
- current provider-refresh evidence, and type the displayed TTY challenge. Before mutation the
8324
- executor writes and verifies a private state recovery point plus a secret-free digest/version
8325
- manifest. The refresh preview validates its provider and later desired-event partitions; any
8326
- ordinary preview failure or malformed post-refresh desired event remains blocked. Provider-only
8327
- observation noise is retained without claiming a replayable preview. Because refresh changes
8328
- recorded state rather than cloud resources, provider-output security and protection findings
8329
- remain explicit review requirements under the drift decision. Refresh never continues into deploy
8330
- or claims desired-state convergence: run the governed deploy target afterward and resolve every
8331
- remaining source-versus-state change. Exclude the \`.private.json\` recovery point from archives.
8332
-
8333
- The root refresh target includes every selected project in its exact reviewed plan set. It skips
8334
- the SST child only when that project's retained plan proves an absent, unlocked, complete, empty,
8335
- and drift-free state, or when the only retained difference is the pinned provider's exact repeated
8336
- SSM \`ebk:framework-version\` \`tagsAll\` normalization. It records the project as an explicit refresh no-op
8337
- in either case while retaining the observation. Value changes, other tags, security findings,
8338
- deletes, replacements, sensitive
8339
- properties, and every other resource type remain material; every other plan must execute or fail
8340
- closed.
8341
-
8342
- ## Router WAF
8343
-
8344
- Each canonical stage owns one global-scope Web ACL attached to that stage's Foundation Router.
8345
- The committed \`security/router-waf.json\` policy requires AWS Common Rule Set, Known Bad
8346
- Inputs, Amazon IP Reputation, an IP rate limit, CloudWatch metrics, and sampled requests.
8347
- Development and staging fail synthesis until their allowlists contain explicit CIDRs.
8348
- Production is public by default and may be changed to an explicit allowlist.
8349
-
8350
- For tuning, set a nonproduction stage to \`count\`, deploy and inspect sampled requests, then
8351
- return staging to \`block\` and run the live allow/deny, managed-rule, rate-limit, metric, and
8352
- sampled-request probes. Write their bounded, secret-free result to
8353
- \`security/router-waf-live-evidence.json\`, then run
8354
- \`pnpm nx run foundation:router-waf-rehearsal\`. The executor seals the exact policy digest
8355
- into \`router-waf-blocking-rehearsal.json\`. Production synthesis rejects a missing, stale, or
8356
- count-mode rehearsal. Full request logging is opt-in, blocked-only, retention-bounded, and
8357
- redacts query strings plus credential-bearing headers.
8358
-
8359
- Foundation stages are canonical. \`ebk dev\` derives a Product Slice personal
8360
- stage from the operating-system account and maps that consumer to the
8361
- development Foundation. Callers cannot select that stage. Slice preflight
8362
- reports both identities, never deploys a personal Foundation, and full mapped
8363
- personal-stage deploys fail closed.
8364
-
8365
- ## Database restore
8366
-
8367
- Production Aurora retains 30 days of automated backups; non-production retains 7 days.
8368
- Restore into a new cluster, validate application migrations and data integrity, then cut over
8369
- consumers deliberately. Never expose the Foundation master credential to a product slice.
8370
-
8371
- ## Protected database migrations
8372
-
8373
- Each database-backed Product Slice deploys a one-off Fargate migration task and
8374
- Standard Step Functions workflow into the shared cluster. The existing
8375
- environment-scoped \`AWS_DEPLOY_ROLE_ARN\` starts and observes that exact
8376
- workflow; do not create a second migration OIDC role. GitHub has no direct SQL,
8377
- database-secret-value, or \`rds-db:connect\` authority. The generated task role
8378
- connects as the Slice's no-password IAM-authenticated migrator user.
8379
8065
  `,
8380
8066
  'packages/foundation/docs/security.md': `# Foundation security contract
8381
8067
 
@@ -8392,7 +8078,7 @@ connects as the Slice's no-password IAM-authenticated migrator user.
8392
8078
  - Every stage's CloudFront Router has its own global Web ACL. The baseline always includes AWS
8393
8079
  Common Rule Set, Known Bad Inputs, Amazon IP Reputation, rate blocking, CloudWatch metrics,
8394
8080
  and sampled requests. Nonproduction is allowlisted by default. Production is public by
8395
- default, but cannot deploy until the exact policy has passed a staging blocking rehearsal.
8081
+ default, but cannot deploy until the exact policy has passed a ${rehearsalStage} blocking rehearsal.
8396
8082
  - Full WAF request logging is disabled by default. Enabling it keeps blocked requests only,
8397
8083
  uses bounded retention, and redacts query strings, cookies, authorization, and API keys.
8398
8084
 
@@ -8433,6 +8119,7 @@ import {
8433
8119
  foundationSsmPath,
8434
8120
  normalizeRouterWafPolicy,
8435
8121
  reconcileRouterWafBlockingRehearsal,
8122
+ routerWafContractDigest,
8436
8123
  type FoundationSsmKey,
8437
8124
  } from '@empire-builder-kit/nx/foundation';
8438
8125
  import {
@@ -8440,12 +8127,19 @@ import {
8440
8127
  FOUNDATION_AURORA_ENGINE_VERSION,
8441
8128
  productionFoundationProfileContract,
8442
8129
  } from '@empire-builder-kit/nx/repository-policy';
8130
+ import { installStageIamBoundaryTransforms } from '@empire-builder-kit/nx/aws-authority';
8131
+ import { createStageAuthorityIdentity } from '@empire-builder-kit/nx/aws-authority';
8132
+ import {
8133
+ configuredDeploymentAccountForStage,
8134
+ configuredDeploymentRegion,
8135
+ productionRehearsalStage,
8136
+ } from '@empire-builder-kit/nx/repository-policy';
8443
8137
  import { createFoundationAccountCostControlPlan } from '@empire-builder-kit/nx/performance';
8444
8138
  import accountCostPolicy from '../performance/account-cost-policy.json';
8445
8139
  import routerWafBlockingRehearsal from '../security/router-waf-blocking-rehearsal.json';
8446
8140
  import routerWafLiveEvidence from '../security/router-waf-live-evidence.json';
8447
8141
  import routerWafPolicy from '../security/router-waf.json';
8448
- import type { FoundationWorkloadRolePreflight } from './workload-roles/preflight.js';
8142
+ import repositoryPolicy from '../../../ebk.repository-policy.json';
8449
8143
 
8450
8144
  function identityDomainPrefix(stage: string, accountId: string): string {
8451
8145
  if (!/^\\d{12}$/.test(accountId)) {
@@ -8479,14 +8173,7 @@ function publishFoundation(values: Record<FoundationSsmKey, any>) {
8479
8173
  );
8480
8174
  }
8481
8175
 
8482
- export function foundation(
8483
- workloadRoles?: FoundationWorkloadRolePreflight,
8484
- ) {
8485
- if (!$dev && !workloadRoles) {
8486
- throw new Error(
8487
- 'Protected Foundation workload-role preflight must complete before infrastructure construction.',
8488
- );
8489
- }
8176
+ export function foundation() {
8490
8177
  const isProduction = $app.stage === 'production';
8491
8178
  const isStaging = $app.stage === 'staging';
8492
8179
  const rootDomain = process.env.EBK_ROOT_DOMAIN?.trim();
@@ -8497,40 +8184,63 @@ export function foundation(
8497
8184
  : undefined;
8498
8185
  const natMode = productionConfiguration?.contract.nat ?? 'ec2';
8499
8186
  const normalizedRouterWafPolicy = normalizeRouterWafPolicy(routerWafPolicy);
8187
+ if (repositoryPolicy.deploymentProfile !== normalizedRouterWafPolicy.deploymentProfile) {
8188
+ throw new Error('Router WAF policy profile differs from committed repository policy.');
8189
+ }
8190
+ const rehearsalStage = productionRehearsalStage(normalizedRouterWafPolicy.deploymentProfile);
8191
+ const rehearsalAccount = configuredDeploymentAccountForStage(
8192
+ normalizedRouterWafPolicy.deploymentProfile,
8193
+ repositoryPolicy.deploymentAccounts,
8194
+ rehearsalStage,
8195
+ );
8196
+ const rehearsalRegion = configuredDeploymentRegion(repositoryPolicy.deploymentRegion);
8197
+ const rehearsalRoleArn = createStageAuthorityIdentity({
8198
+ accountId: rehearsalAccount,
8199
+ partition: rehearsalRegion.startsWith('us-gov-') ? 'aws-us-gov' : 'aws',
8200
+ region: rehearsalRegion,
8201
+ stage: rehearsalStage,
8202
+ }).deploymentRoleArn;
8500
8203
  const wafPlan = createRouterWafStagePlan({
8501
8204
  policy: normalizedRouterWafPolicy,
8502
8205
  rehearsal: isProduction
8503
8206
  ? reconcileRouterWafBlockingRehearsal({
8504
8207
  evidence: routerWafLiveEvidence,
8208
+ expected: {
8209
+ accountId: rehearsalAccount,
8210
+ deploymentRoleArn: rehearsalRoleArn,
8211
+ region: rehearsalRegion,
8212
+ stage: rehearsalStage,
8213
+ wafContractDigest: routerWafContractDigest(normalizedRouterWafPolicy),
8214
+ workspace: '${name}',
8215
+ },
8505
8216
  policy: normalizedRouterWafPolicy,
8506
8217
  receipt: routerWafBlockingRehearsal,
8507
8218
  })
8508
8219
  : null,
8509
8220
  stage: $app.stage,
8510
8221
  });
8511
- const natBinding = workloadRoles?.bindings.nat;
8512
- if (natMode === 'ec2' && natBinding) {
8513
- $transform(
8514
- aws.iam.InstanceProfile,
8515
- (args: any, opts: any, logicalName: string) => {
8516
- if (logicalName !== 'FoundationVpcNatInstanceProfile') return;
8517
- args.name = natBinding.instanceProfileName;
8518
- args.role = natBinding.roleName;
8519
- opts.import = natBinding.instanceProfileName;
8520
- opts.ignoreChanges = [
8521
- ...new Set([...(opts.ignoreChanges ?? []), 'tags', 'tagsAll']),
8522
- ];
8523
- },
8222
+ const deploymentAccount = process.env.AWS_EXPECTED_ACCOUNT_ID?.trim() ?? '';
8223
+ if (!/^\\d{12}$/.test(deploymentAccount)) {
8224
+ throw new Error(
8225
+ 'Foundation deployment requires the exact committed AWS account before infrastructure construction.',
8524
8226
  );
8525
8227
  }
8228
+ installStageIamBoundaryTransforms({
8229
+ accountId: deploymentAccount,
8230
+ instanceProfileResource: aws.iam.InstanceProfile,
8231
+ registerTransform: $transform,
8232
+ roleResource: aws.iam.Role,
8233
+ stage: ($dev ? 'development' : $app.stage) as
8234
+ | 'development'
8235
+ | 'staging'
8236
+ | 'production',
8237
+ });
8526
8238
 
8527
8239
  const vpc = new sst.aws.Vpc('FoundationVpc', {
8528
8240
  az: 2,
8529
8241
  bastion: $app.stage === 'development',
8530
8242
  nat:
8531
- natMode === 'ec2'
8532
- ? (natBinding?.vpc.nat ?? 'ec2')
8533
- : 'managed',
8243
+ natMode === 'ec2' ? 'ec2' : 'managed',
8534
8244
  });
8535
8245
  const cluster = new sst.aws.Cluster('FoundationCluster', { vpc });
8536
8246
  const router = new sst.aws.Router('FoundationRouter', {
@@ -8559,6 +8269,13 @@ export function foundation(
8559
8269
  'Foundation Router must attach its exact global Web ACL.',
8560
8270
  );
8561
8271
  }
8272
+ args.tags = {
8273
+ ...(args.tags ?? {}),
8274
+ 'ebk:managed-by': 'empire-builder-kit',
8275
+ 'ebk:owner': 'foundation',
8276
+ 'ebk:stage': wafPlan.stage,
8277
+ 'ebk:workspace': '${name}',
8278
+ };
8562
8279
  },
8563
8280
  waf(args: any, opts: any) {
8564
8281
  if (args.scope !== 'CLOUDFRONT') {
@@ -8576,8 +8293,11 @@ export function foundation(
8576
8293
  name: 'foundation-' + wafPlan.stage + '-router-ipv4-allowlist',
8577
8294
  scope: 'CLOUDFRONT',
8578
8295
  tags: {
8296
+ 'ebk:managed-by': 'empire-builder-kit',
8297
+ 'ebk:owner': 'foundation',
8579
8298
  'ebk:policy-digest': wafPlan.policyDigest,
8580
8299
  'ebk:stage': wafPlan.stage,
8300
+ 'ebk:workspace': '${name}',
8581
8301
  },
8582
8302
  },
8583
8303
  { provider: opts.provider },
@@ -8592,8 +8312,11 @@ export function foundation(
8592
8312
  name: 'foundation-' + wafPlan.stage + '-router-ipv6-allowlist',
8593
8313
  scope: 'CLOUDFRONT',
8594
8314
  tags: {
8315
+ 'ebk:managed-by': 'empire-builder-kit',
8316
+ 'ebk:owner': 'foundation',
8595
8317
  'ebk:policy-digest': wafPlan.policyDigest,
8596
8318
  'ebk:stage': wafPlan.stage,
8319
+ 'ebk:workspace': '${name}',
8597
8320
  },
8598
8321
  },
8599
8322
  { provider: opts.provider },
@@ -8604,8 +8327,11 @@ export function foundation(
8604
8327
  args.scope = 'CLOUDFRONT';
8605
8328
  args.tags = {
8606
8329
  ...(args.tags ?? {}),
8330
+ 'ebk:managed-by': 'empire-builder-kit',
8331
+ 'ebk:owner': 'foundation',
8607
8332
  'ebk:policy-digest': wafPlan.policyDigest,
8608
8333
  'ebk:stage': wafPlan.stage,
8334
+ 'ebk:workspace': '${name}',
8609
8335
  };
8610
8336
  args.visibilityConfig = {
8611
8337
  cloudwatchMetricsEnabled: true,
@@ -8709,6 +8435,7 @@ export function foundation(
8709
8435
  const accountCostControl = createFoundationAccountCostControlPlan({
8710
8436
  evaluatedAt: new Date().toISOString(),
8711
8437
  policy: accountCostPolicy,
8438
+ rehearsalStage: '${rehearsalStage}',
8712
8439
  region: process.env.AWS_REGION,
8713
8440
  stage: $app.stage,
8714
8441
  workspace: '${name}',
@@ -9013,15 +8740,9 @@ export function foundation(
9013
8740
  }
9014
8741
  `,
9015
8742
  'packages/foundation/infra/index.ts': `import { foundation } from './foundation.js';
9016
- import type { FoundationWorkloadRolePreflight } from './workload-roles/preflight.js';
9017
8743
 
9018
- export function app(workloadRoles?: FoundationWorkloadRolePreflight) {
9019
- const resources = foundation(workloadRoles);
9020
- return {
9021
- ...resources,
9022
- workloadRoleManifestDigest:
9023
- workloadRoles?.registry.manifestDigest ?? 'local-dev-ephemeral',
9024
- };
8744
+ export function app() {
8745
+ return foundation();
9025
8746
  }
9026
8747
  `,
9027
8748
  'packages/foundation/performance/account-cost-policy.json': json({
@@ -9036,6 +8757,7 @@ export function app(workloadRoles?: FoundationWorkloadRolePreflight) {
9036
8757
  workspace: name,
9037
8758
  }),
9038
8759
  'packages/foundation/security/router-waf.json': json({
8760
+ deploymentProfile,
9039
8761
  kind: 'ebk-router-waf-policy',
9040
8762
  logging: {
9041
8763
  enabled: false,
@@ -9054,21 +8776,25 @@ export function app(workloadRoles?: FoundationWorkloadRolePreflight) {
9054
8776
  allowedCidrs: [],
9055
8777
  mode: 'block',
9056
8778
  },
9057
- staging: {
9058
- access: 'allowlist',
9059
- allowedCidrs: [],
9060
- mode: 'block',
9061
- },
8779
+ ...(deploymentProfile === 'starter'
8780
+ ? {}
8781
+ : {
8782
+ staging: {
8783
+ access: 'allowlist',
8784
+ allowedCidrs: [],
8785
+ mode: 'block',
8786
+ },
8787
+ }),
9062
8788
  },
9063
8789
  }),
9064
8790
  'packages/foundation/security/router-waf-blocking-rehearsal.json': json({
9065
8791
  kind: 'ebk-router-waf-blocking-rehearsal',
9066
- schemaVersion: 1,
8792
+ schemaVersion: 4,
9067
8793
  status: 'pending',
9068
8794
  }),
9069
8795
  'packages/foundation/security/router-waf-live-evidence.json': json({
9070
8796
  kind: 'ebk-router-waf-live-evidence',
9071
- schemaVersion: 1,
8797
+ schemaVersion: 4,
9072
8798
  status: 'pending',
9073
8799
  }),
9074
8800
  'packages/foundation/configuration/declarations.json': json({
@@ -9103,16 +8829,7 @@ export function app(workloadRoles?: FoundationWorkloadRolePreflight) {
9103
8829
  version: '4.17.1',
9104
8830
  },
9105
8831
  policy: {
9106
- adoptedImports: [
9107
- {
9108
- authority: 'workload-role-account-baseline',
9109
- logicalId: 'FoundationVpcNatInstanceProfile',
9110
- privateIdentifierTemplate: 'ebk-{stage}-foundation-nat-profile',
9111
- recoveryRunbookRef: 'docs/workload-role-account-baseline.md',
9112
- removalBehavior: 'retain',
9113
- resourceType: 'aws:iam/instanceProfile:InstanceProfile',
9114
- },
9115
- ],
8832
+ adoptedImports: [],
9116
8833
  additiveLowRiskTypes: [
9117
8834
  'aws:cloudwatch/dashboard:Dashboard',
9118
8835
  'aws:cloudwatch/logGroup:LogGroup',
@@ -9222,10 +8939,6 @@ export function app(workloadRoles?: FoundationWorkloadRolePreflight) {
9222
8939
  'verify',
9223
8940
  { target: 'profile-preflight', params: 'forward' },
9224
8941
  ],
9225
- outputs: [
9226
- `{projectRoot}/.ebk/evidence/infrastructure-convergence.json`,
9227
- `{projectRoot}/.ebk/evidence/infrastructure-mutation-binding.json`,
9228
- ],
9229
8942
  options: {
9230
8943
  cwd: 'packages/foundation',
9231
8944
  stage: 'development',
@@ -9242,15 +8955,7 @@ export function app(workloadRoles?: FoundationWorkloadRolePreflight) {
9242
8955
  'infrastructure-plan': {
9243
8956
  cache: false,
9244
8957
  executor: '@empire-builder-kit/nx:infrastructure-plan',
9245
- outputs: [
9246
- `{projectRoot}/.ebk/evidence/infrastructure-plan-authorization.json`,
9247
- `{projectRoot}/.ebk/evidence/infrastructure-plan-command.json`,
9248
- `{projectRoot}/.ebk/evidence/infrastructure-plan.private-stderr.txt`,
9249
- `{projectRoot}/.ebk/evidence/infrastructure-plan.raw.json`,
9250
- `{projectRoot}/.ebk/evidence/infrastructure-plan.json`,
9251
- ],
9252
8958
  options: {
9253
- action: 'deploy',
9254
8959
  cwd: 'packages/foundation',
9255
8960
  stage: 'development',
9256
8961
  stageMode: 'foundation',
@@ -9259,10 +8964,6 @@ export function app(workloadRoles?: FoundationWorkloadRolePreflight) {
9259
8964
  refresh: {
9260
8965
  executor: '@empire-builder-kit/nx:refresh',
9261
8966
  dependsOn: [{ target: 'profile-preflight', params: 'forward' }],
9262
- outputs: [
9263
- `{projectRoot}/.ebk/evidence/infrastructure-convergence.json`,
9264
- `{projectRoot}/.ebk/evidence/infrastructure-mutation-binding.json`,
9265
- ],
9266
8967
  options: {
9267
8968
  cwd: 'packages/foundation',
9268
8969
  stage: 'development',
@@ -9272,10 +8973,6 @@ export function app(workloadRoles?: FoundationWorkloadRolePreflight) {
9272
8973
  remove: {
9273
8974
  executor: '@empire-builder-kit/nx:remove',
9274
8975
  dependsOn: [{ target: 'remove-preflight', params: 'forward' }],
9275
- outputs: [
9276
- `{projectRoot}/.ebk/evidence/infrastructure-convergence.json`,
9277
- `{projectRoot}/.ebk/evidence/infrastructure-mutation-binding.json`,
9278
- ],
9279
8976
  options: {
9280
8977
  cwd: 'packages/foundation',
9281
8978
  stage: 'development',
@@ -9325,9 +9022,17 @@ export function app(workloadRoles?: FoundationWorkloadRolePreflight) {
9325
9022
  'packages/foundation/scripts/verify-foundation.mjs': `import { readFileSync } from 'node:fs';
9326
9023
 
9327
9024
  import {
9025
+ normalizeRouterWafBlockingRehearsal,
9328
9026
  normalizeRouterWafPolicy,
9329
9027
  reconcileRouterWafBlockingRehearsal,
9028
+ routerWafContractDigest,
9330
9029
  } from '@empire-builder-kit/nx/foundation';
9030
+ import { createStageAuthorityIdentity } from '@empire-builder-kit/nx/aws-authority';
9031
+ import {
9032
+ configuredDeploymentAccountForStage,
9033
+ configuredDeploymentRegion,
9034
+ productionRehearsalStage,
9035
+ } from '@empire-builder-kit/nx/repository-policy';
9331
9036
 
9332
9037
  const descriptor = JSON.parse(
9333
9038
  readFileSync(new URL('../contracts/foundation.json', import.meta.url), 'utf8'),
@@ -9352,25 +9057,54 @@ const routerWafPolicy = normalizeRouterWafPolicy(
9352
9057
  ),
9353
9058
  ),
9354
9059
  );
9355
- reconcileRouterWafBlockingRehearsal({
9356
- evidence: JSON.parse(
9357
- readFileSync(
9358
- new URL('../security/router-waf-live-evidence.json', import.meta.url),
9359
- 'utf8',
9060
+ const repositoryPolicy = JSON.parse(
9061
+ readFileSync(new URL('../../../ebk.repository-policy.json', import.meta.url), 'utf8'),
9062
+ );
9063
+ const workspaceManifest = JSON.parse(
9064
+ readFileSync(new URL('../../../package.json', import.meta.url), 'utf8'),
9065
+ );
9066
+ const rehearsalReceipt = JSON.parse(
9067
+ readFileSync(
9068
+ new URL(
9069
+ '../security/router-waf-blocking-rehearsal.json',
9070
+ import.meta.url,
9360
9071
  ),
9072
+ 'utf8',
9361
9073
  ),
9362
- policy: routerWafPolicy,
9363
- receipt:
9364
- JSON.parse(
9365
- readFileSync(
9366
- new URL(
9367
- '../security/router-waf-blocking-rehearsal.json',
9368
- import.meta.url,
9074
+ );
9075
+ const normalizedRehearsal = normalizeRouterWafBlockingRehearsal(rehearsalReceipt);
9076
+ if (normalizedRehearsal) {
9077
+ const rehearsalStage = productionRehearsalStage(repositoryPolicy.deploymentProfile);
9078
+ const rehearsalAccount = configuredDeploymentAccountForStage(
9079
+ repositoryPolicy.deploymentProfile,
9080
+ repositoryPolicy.deploymentAccounts,
9081
+ rehearsalStage,
9082
+ );
9083
+ const rehearsalRegion = configuredDeploymentRegion(repositoryPolicy.deploymentRegion);
9084
+ reconcileRouterWafBlockingRehearsal({
9085
+ evidence: JSON.parse(
9086
+ readFileSync(
9087
+ new URL('../security/router-waf-live-evidence.json', import.meta.url),
9088
+ 'utf8',
9369
9089
  ),
9370
- 'utf8',
9371
9090
  ),
9372
- ),
9373
- });
9091
+ expected: {
9092
+ accountId: rehearsalAccount,
9093
+ deploymentRoleArn: createStageAuthorityIdentity({
9094
+ accountId: rehearsalAccount,
9095
+ partition: rehearsalRegion.startsWith('us-gov-') ? 'aws-us-gov' : 'aws',
9096
+ region: rehearsalRegion,
9097
+ stage: rehearsalStage,
9098
+ }).deploymentRoleArn,
9099
+ region: rehearsalRegion,
9100
+ stage: rehearsalStage,
9101
+ wafContractDigest: routerWafContractDigest(routerWafPolicy),
9102
+ workspace: workspaceManifest.name,
9103
+ },
9104
+ policy: routerWafPolicy,
9105
+ receipt: normalizedRehearsal,
9106
+ });
9107
+ }
9374
9108
  const expectedCapabilities = [
9375
9109
  'network',
9376
9110
  'routing',
@@ -9387,8 +9121,10 @@ if (descriptor.contractVersion !== '1.0.0') {
9387
9121
  throw new Error('Foundation contractVersion must be 1.0.0.');
9388
9122
  }
9389
9123
  if (
9124
+ JSON.stringify(Object.keys(routerWafPolicy.stages).sort()) !==
9125
+ JSON.stringify(${JSON.stringify([...activeStages].sort())}) ||
9390
9126
  routerWafPolicy.stages.development.access !== 'allowlist' ||
9391
- routerWafPolicy.stages.staging.access !== 'allowlist' ||
9127
+ ${deploymentProfile !== 'starter' ? "routerWafPolicy.stages.staging?.access !== 'allowlist' ||" : ''}
9392
9128
  routerWafPolicy.stages.production.mode !== 'block'
9393
9129
  ) {
9394
9130
  throw new Error('Foundation Router WAF stage defaults are invalid.');
@@ -9403,7 +9139,8 @@ if (
9403
9139
  costs.productionProfiles?.standard?.aurora?.min !== '2 ACU' ||
9404
9140
  costs.productionProfiles?.['cost-optimized']?.aurora?.min !== '0.5 ACU' ||
9405
9141
  !costs.stages?.development ||
9406
- !costs.stages?.staging ||
9142
+ JSON.stringify(Object.keys(costs.stages ?? {}).sort()) !==
9143
+ JSON.stringify(${JSON.stringify([...activeStages].sort())}) ||
9407
9144
  costs.stages?.production?.dataRetention !== 'retained-and-protected'
9408
9145
  ) {
9409
9146
  throw new Error('Foundation cost manifest does not match the v1 stage contract.');
@@ -9443,9 +9180,9 @@ export {};
9443
9180
  export default $config({
9444
9181
  app(input: { stage?: string } | undefined) {
9445
9182
  const stage = input?.stage ?? 'development';
9446
- if (!['development', 'staging', 'production'].includes(stage)) {
9183
+ if (!new Set<string>(${JSON.stringify(activeStages)}).has(stage)) {
9447
9184
  throw new Error(
9448
- 'Foundation stage ' + stage + ' is not canonical. Use development, staging, or production.',
9185
+ 'Foundation stage ' + stage + ' is inactive for the ${deploymentProfile} deployment profile.',
9449
9186
  );
9450
9187
  }
9451
9188
  const production = stage === 'production';
@@ -9477,15 +9214,8 @@ export default $config({
9477
9214
  };
9478
9215
  },
9479
9216
  async run() {
9480
- const workloadRoles = $dev
9481
- ? undefined
9482
- : await import(
9483
- './infra/workload-roles/preflight.js'
9484
- ).then(({ preflightFoundationWorkloadRoles }) =>
9485
- preflightFoundationWorkloadRoles($app.stage),
9486
- );
9487
9217
  const { app } = await import('./infra/index.js');
9488
- return app(workloadRoles);
9218
+ return app();
9489
9219
  },
9490
9220
  });
9491
9221
  `,
@@ -10419,7 +10149,7 @@ const decision = evaluateGitPolicy(request);
10419
10149
  process.stdout.write(JSON.stringify(decision, null, 2) + '\\n');
10420
10150
  if (!decision.allowed) process.exitCode = 1;
10421
10151
  `,
10422
- 'tools/workspace-health.mjs': `import { validateRepositoryOperatingPolicy } from '@empire-builder-kit/nx/repository-policy';
10152
+ 'tools/workspace-health.mjs': `import { readRepositoryOperatingPolicy } from '@empire-builder-kit/nx/repository-policy';
10423
10153
  import { assertWorkMarkerReport, formatWorkMarkerReport, scanWorkMarkers } from '@empire-builder-kit/nx/work-markers';
10424
10154
  import { createHash } from 'node:crypto';
10425
10155
  import { existsSync, readdirSync, readFileSync } from 'node:fs';
@@ -10482,13 +10212,7 @@ if (
10482
10212
  validateOwnership('.', workspace, 'Workspace');
10483
10213
  validateOwnership('packages/foundation', foundation, 'Foundation');
10484
10214
 
10485
- const policy = validateRepositoryOperatingPolicy(readJson('ebk.repository-policy.json'));
10486
- if (!policy.valid) {
10487
- throw new Error(
10488
- 'Repository Operating Policy is invalid:\\n' +
10489
- policy.issues.map(({ message, path }) => '- ' + path + ': ' + message).join('\\n'),
10490
- );
10491
- }
10215
+ readRepositoryOperatingPolicy(process.cwd());
10492
10216
 
10493
10217
  for (const name of readdirSync('packages')) {
10494
10218
  if (name === 'foundation') continue;
@@ -10506,7 +10230,51 @@ assertWorkMarkerReport(markerReport);
10506
10230
  process.stdout.write('Empire Builder Kit workspace source health is green.\\n');
10507
10231
  `,
10508
10232
  };
10509
- 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 }));
10233
+ Object.assign(files, SUPPLY_CHAIN_WORKSPACE_FILE_PLAN.files, developerAgentFilePlan.files, (0, workspace_files_stage_authority_js_1.renderStageAuthorityWorkspaceFiles)({
10234
+ deploymentProfile,
10235
+ name,
10236
+ workspaceDeploymentId,
10237
+ }));
10238
+ Object.assign(files, (0, workspace_files_ecr_inspector_js_1.renderThinEcrInspectorWorkflow)({
10239
+ actionPins: {
10240
+ checkout: checkoutAction,
10241
+ configureAwsCredentials: configureAwsAction,
10242
+ downloadArtifact: downloadArtifactAction,
10243
+ pnpmSetup: pnpmAction,
10244
+ setupNode: setupNodeAction,
10245
+ uploadArtifact: uploadArtifactAction,
10246
+ },
10247
+ deploymentProfile,
10248
+ name,
10249
+ nodeVersion: tool_versions_js_1.WORKSPACE_NODE_VERSION,
10250
+ }), (0, workspace_files_thin_deploy_js_1.renderThinDeployWorkflowFilePlan)({
10251
+ actionPins: {
10252
+ checkout: checkoutAction,
10253
+ configureAwsCredentials: configureAwsAction,
10254
+ downloadArtifact: downloadArtifactAction,
10255
+ pnpmSetup: pnpmAction,
10256
+ setupNode: setupNodeAction,
10257
+ uploadArtifact: uploadArtifactAction,
10258
+ },
10259
+ deploymentProfile,
10260
+ name,
10261
+ toolVersions: {
10262
+ node: tool_versions_js_1.WORKSPACE_NODE_VERSION,
10263
+ sst: tool_versions_js_1.WORKSPACE_SST_VERSION,
10264
+ },
10265
+ }).files, (0, workspace_files_thin_docs_js_1.renderThinLifecycleDocumentation)({ deploymentProfile, name }));
10266
+ const thinReleaseWorkflowPlan = (0, workspace_files_thin_release_js_1.renderThinReleaseWorkflowPlan)();
10267
+ Object.assign(files, thinReleaseWorkflowPlan.files);
10268
+ for (const obsoletePath of [
10269
+ ...thinReleaseWorkflowPlan.obsoletePaths,
10270
+ '.github/workflows/performance-aggregate-counts.yml',
10271
+ '.github/workflows/performance-cost-actual.yml',
10272
+ '.github/workflows/performance-load.yml',
10273
+ 'packages/foundation/operations/infrastructure-change.json',
10274
+ 'tools/ebk-infrastructure-plan-summary.mjs',
10275
+ ]) {
10276
+ delete files[obsoletePath];
10277
+ }
10510
10278
  for (const [path, source] of Object.entries(files)) {
10511
10279
  if (/^\.github\/workflows\/[^/]+\.ya?ml$/u.test(path)) {
10512
10280
  files[path] = isolateOidcTokenBeforeAwsAuthority(source, configureAwsAction);