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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (791) hide show
  1. package/README.md +18 -0
  2. package/executors.json +21 -1
  3. package/generators.json +10 -0
  4. package/package.json +31 -7
  5. package/src/aws/child-environment.d.ts +10 -0
  6. package/src/aws/child-environment.js +77 -0
  7. package/src/aws/identity.d.ts +13 -0
  8. package/src/aws/identity.js +41 -0
  9. package/src/aws-authority/index.d.ts +2 -0
  10. package/src/aws-authority/index.js +6 -0
  11. package/src/aws-authority/lambda-policy-ordering.d.ts +38 -0
  12. package/src/aws-authority/lambda-policy-ordering.js +56 -0
  13. package/src/aws-authority/stage-authority.d.ts +248 -0
  14. package/src/aws-authority/stage-authority.js +3558 -0
  15. package/src/blueprints/external-sandbox.js +21 -9
  16. package/src/blueprints/manifest.d.ts +1 -1
  17. package/src/blueprints/resolver.js +28 -15
  18. package/src/cli/bootstrap.d.ts +1 -1
  19. package/src/cli/deploy-runtime.d.ts +6 -0
  20. package/src/cli/deploy-runtime.js +58 -0
  21. package/src/cli/dev-command.d.ts +16 -4
  22. package/src/cli/dev-command.js +63 -0
  23. package/src/cli/main.d.ts +9 -1
  24. package/src/cli/main.js +139 -20
  25. package/src/cli/output.d.ts +1 -1
  26. package/src/cli/output.js +1 -1
  27. package/src/developer-agent/context.js +41 -9
  28. package/src/developer-agent/generator-planning.js +20 -6
  29. package/src/developer-agent/git-inspection.d.ts +6 -2
  30. package/src/developer-agent/git-inspection.js +78 -6
  31. package/src/developer-agent/model.d.ts +5 -3
  32. package/src/developer-agent/model.js +4 -4
  33. package/src/developer-agent/operation-registry.d.ts +5 -0
  34. package/src/developer-agent/operation-registry.js +286 -40
  35. package/src/developer-agent/planning.js +16 -13
  36. package/src/developer-agent/preview-changes.d.ts +2 -0
  37. package/src/developer-agent/preview-changes.js +14 -2
  38. package/src/developer-agent/preview-source.js +3 -0
  39. package/src/developer-agent/validation.js +25 -9
  40. package/src/developer-agent/workspace-inspection.js +60 -5
  41. package/src/executors/blueprint-trust/blueprint-trust.js +6 -0
  42. package/src/executors/deployment-plan/deployment-plan.d.ts +2 -0
  43. package/src/executors/deployment-plan/deployment-plan.js +57 -2
  44. package/src/executors/dev-doctor/dev-doctor.js +13 -6
  45. package/src/executors/dev-task-cleanup/dev-task-cleanup.d.ts +23 -0
  46. package/src/executors/dev-task-cleanup/dev-task-cleanup.js +145 -0
  47. package/src/executors/dev-task-cleanup/schema.json +9 -0
  48. package/src/executors/dev-task-cleanup/task-stack-absence.d.ts +27 -0
  49. package/src/executors/dev-task-cleanup/task-stack-absence.js +281 -0
  50. package/src/executors/dev-task-cleanup/task-stack-checkpoint.d.ts +12 -0
  51. package/src/executors/dev-task-cleanup/task-stack-checkpoint.js +92 -0
  52. package/src/executors/fleet/fleet.js +1 -1
  53. package/src/executors/foundation-preflight/foundation-preflight.d.ts +1 -0
  54. package/src/executors/foundation-preflight/foundation-preflight.js +6 -2
  55. package/src/executors/foundation-preflight/schema.json +5 -0
  56. package/src/executors/foundation-profile-preflight/foundation-profile-preflight.d.ts +13 -2
  57. package/src/executors/foundation-profile-preflight/foundation-profile-preflight.js +95 -31
  58. package/src/executors/foundation-profile-preflight/production-database-safeguard.d.ts +65 -0
  59. package/src/executors/foundation-profile-preflight/production-database-safeguard.js +223 -0
  60. package/src/executors/foundation-profile-preflight/schema.json +15 -4
  61. package/src/executors/foundation-remove-preflight/foundation-remove-preflight.d.ts +11 -1
  62. package/src/executors/foundation-remove-preflight/foundation-remove-preflight.js +70 -20
  63. package/src/executors/foundation-remove-preflight/schema.json +6 -1
  64. package/src/executors/github-bootstrap/github-bootstrap.d.ts +1 -0
  65. package/src/executors/github-bootstrap/github-bootstrap.js +3 -0
  66. package/src/executors/github-bootstrap/schema.json +7 -1
  67. package/src/executors/infrastructure-plan/infrastructure-plan.d.ts +29 -136
  68. package/src/executors/infrastructure-plan/infrastructure-plan.js +230 -3282
  69. package/src/executors/infrastructure-plan/schema.json +12 -52
  70. package/src/executors/local-deploy/federated-credential-transport.d.ts +48 -0
  71. package/src/executors/local-deploy/federated-credential-transport.js +252 -0
  72. package/src/executors/local-deploy/local-approval.d.ts +18 -0
  73. package/src/executors/local-deploy/local-approval.js +39 -0
  74. package/src/executors/local-deploy/local-deploy.d.ts +112 -0
  75. package/src/executors/local-deploy/local-deploy.js +1404 -0
  76. package/src/executors/local-deploy/schema.json +70 -0
  77. package/src/executors/refresh/refresh.js +2 -2
  78. package/src/executors/remove/remove.js +2 -2
  79. package/src/executors/router-waf-rehearsal/router-waf-rehearsal.d.ts +42 -0
  80. package/src/executors/router-waf-rehearsal/router-waf-rehearsal.js +1229 -0
  81. package/src/executors/router-waf-rehearsal/schema.json +29 -0
  82. package/src/executors/secret-initialize/schema.json +42 -0
  83. package/src/executors/secret-initialize/secret-initialize.d.ts +34 -0
  84. package/src/executors/secret-initialize/secret-initialize.js +297 -0
  85. package/src/executors/secret-metadata/schema.json +5 -0
  86. package/src/executors/secret-metadata/secret-metadata.d.ts +16 -2
  87. package/src/executors/secret-metadata/secret-metadata.js +129 -30
  88. package/src/executors/sst-lifecycle/generated-secret-retirement.d.ts +53 -0
  89. package/src/executors/sst-lifecycle/generated-secret-retirement.js +192 -0
  90. package/src/executors/sst-lifecycle/governed-native-operation.d.ts +48 -0
  91. package/src/executors/sst-lifecycle/governed-native-operation.js +162 -0
  92. package/src/executors/sst-lifecycle/logging-preflight.d.ts +5 -0
  93. package/src/executors/sst-lifecycle/logging-preflight.js +23 -13
  94. package/src/executors/sst-lifecycle/run-sst-command.d.ts +40 -6
  95. package/src/executors/sst-lifecycle/run-sst-command.js +779 -70
  96. package/src/executors/sst-lifecycle/schema.json +8 -1
  97. package/src/executors/sst-lifecycle/secret-preflight.d.ts +2 -0
  98. package/src/executors/sst-lifecycle/secret-preflight.js +13 -7
  99. package/src/executors/sst-lifecycle/sst-environment-source.d.ts +33 -0
  100. package/src/executors/sst-lifecycle/sst-environment-source.js +267 -0
  101. package/src/executors/sst-lifecycle/task-unlock.d.ts +15 -0
  102. package/src/executors/sst-lifecycle/task-unlock.js +126 -0
  103. package/src/executors/unlock/unlock.js +2 -2
  104. package/src/foundation/capability-plan.d.ts +43 -0
  105. package/src/foundation/capability-plan.js +168 -0
  106. package/src/foundation/discovery.d.ts +14 -11
  107. package/src/foundation/discovery.js +32 -6
  108. package/src/foundation/index.d.ts +4 -0
  109. package/src/foundation/index.js +11 -0
  110. package/src/foundation/local-database-contract.d.ts +20 -0
  111. package/src/foundation/local-database-contract.js +262 -13
  112. package/src/foundation/local-private-smoke-access.d.ts +20 -0
  113. package/src/foundation/local-private-smoke-access.js +197 -0
  114. package/src/foundation/logging-preflight.js +1 -1
  115. package/src/foundation/logging.d.ts +3 -1
  116. package/src/foundation/logging.js +1 -1
  117. package/src/foundation/partial-state.d.ts +10 -0
  118. package/src/foundation/partial-state.js +21 -0
  119. package/src/foundation/preflight.js +37 -18
  120. package/src/foundation/private-smoke-access.d.ts +25 -0
  121. package/src/foundation/private-smoke-access.js +135 -0
  122. package/src/foundation/private-smoke-admission.d.ts +45 -0
  123. package/src/foundation/private-smoke-admission.js +180 -0
  124. package/src/foundation/private-smoke-control-plane.d.ts +18 -0
  125. package/src/foundation/private-smoke-control-plane.js +184 -0
  126. package/src/foundation/provision-database-bindings.d.ts +5 -0
  127. package/src/foundation/provision-database-bindings.js +41 -8
  128. package/src/foundation/provision-logical-database.js +15 -8
  129. package/src/foundation/requirements.d.ts +3 -3
  130. package/src/foundation/requirements.js +24 -12
  131. package/src/foundation/router-waf-rate-rehearsal.d.ts +97 -0
  132. package/src/foundation/router-waf-rate-rehearsal.js +210 -0
  133. package/src/foundation/router-waf.d.ts +149 -0
  134. package/src/foundation/router-waf.js +809 -0
  135. package/src/foundation/stages.d.ts +1 -0
  136. package/src/foundation/stages.js +11 -0
  137. package/src/generators/blueprint/blueprint.d.ts +5 -0
  138. package/src/generators/blueprint/blueprint.js +102 -0
  139. package/src/generators/blueprint/files.d.ts +6 -0
  140. package/src/generators/blueprint/files.js +217 -0
  141. package/src/generators/blueprint/schema.d.ts +4 -0
  142. package/src/generators/blueprint/schema.json +18 -0
  143. package/src/generators/formatting.d.ts +8 -0
  144. package/src/generators/formatting.js +141 -0
  145. package/src/generators/function/function.js +38 -17
  146. package/src/generators/logging-registry.d.ts +2 -0
  147. package/src/generators/logging-registry.js +22 -0
  148. package/src/generators/post-generation-install.js +2 -2
  149. package/src/generators/preset/dependency-overrides.d.ts +8 -0
  150. package/src/generators/preset/dependency-overrides.js +55 -0
  151. package/src/generators/preset/generator.js +43 -3
  152. package/src/generators/preset/schema.d.ts +10 -0
  153. package/src/generators/preset/schema.json +47 -0
  154. package/src/generators/preset/tool-versions.d.ts +2 -0
  155. package/src/generators/preset/tool-versions.js +3 -1
  156. package/src/generators/preset/workspace-files-adopter-skills.d.ts +7 -0
  157. package/src/generators/preset/workspace-files-adopter-skills.js +303 -0
  158. package/src/generators/preset/workspace-files-ci-acceptance.d.ts +1 -0
  159. package/src/generators/preset/workspace-files-ci-acceptance.js +791 -0
  160. package/src/generators/preset/workspace-files-ci-install.d.ts +2 -0
  161. package/src/generators/preset/workspace-files-ci-install.js +196 -0
  162. package/src/generators/preset/workspace-files-developer-agent.d.ts +2 -0
  163. package/src/generators/preset/workspace-files-developer-agent.js +90 -28
  164. package/src/generators/preset/workspace-files-ecr-inspector.d.ts +18 -0
  165. package/src/generators/preset/workspace-files-ecr-inspector.js +298 -0
  166. package/src/generators/preset/workspace-files-foundation-workload-roles.js +5 -2
  167. package/src/generators/preset/workspace-files-performance-cost.d.ts +2 -2
  168. package/src/generators/preset/workspace-files-performance-cost.js +8 -8
  169. package/src/generators/preset/workspace-files-performance-load.d.ts +1 -1
  170. package/src/generators/preset/workspace-files-performance-load.js +39 -25
  171. package/src/generators/preset/workspace-files-stage-authority.d.ts +6 -0
  172. package/src/generators/preset/workspace-files-stage-authority.js +518 -0
  173. package/src/generators/preset/workspace-files-thin-deploy.d.ts +55 -0
  174. package/src/generators/preset/workspace-files-thin-deploy.js +2473 -0
  175. package/src/generators/preset/workspace-files-thin-docs.d.ts +7 -0
  176. package/src/generators/preset/workspace-files-thin-docs.js +352 -0
  177. package/src/generators/preset/workspace-files-thin-release.d.ts +14 -0
  178. package/src/generators/preset/workspace-files-thin-release.js +26 -0
  179. package/src/generators/preset/workspace-files-workload-role-baseline.js +254 -42
  180. package/src/generators/preset/workspace-files.d.ts +18 -2
  181. package/src/generators/preset/workspace-files.js +1504 -608
  182. package/src/generators/repository-policy/repository-policy.js +235 -5
  183. package/src/generators/repository-policy/schema.d.ts +10 -0
  184. package/src/generators/repository-policy/schema.json +80 -4
  185. package/src/generators/repository-policy/tree-policy.d.ts +3 -0
  186. package/src/generators/repository-policy/tree-policy.js +25 -0
  187. package/src/generators/slice/built-in-plan-api-contract-files.js +42 -7
  188. package/src/generators/slice/built-in-plan-api-files.d.ts +1 -1
  189. package/src/generators/slice/built-in-plan-api-files.js +130 -414
  190. package/src/generators/slice/built-in-plan-async-work-files.js +166 -1969
  191. package/src/generators/slice/built-in-plan-authorization-files.js +92 -329
  192. package/src/generators/slice/built-in-plan-authorization-infrastructure-files.js +60 -2
  193. package/src/generators/slice/built-in-plan-authorization-storage.d.ts +3 -0
  194. package/src/generators/slice/built-in-plan-authorization-storage.js +119 -0
  195. package/src/generators/slice/built-in-plan-background-work-files.d.ts +2 -0
  196. package/src/generators/slice/built-in-plan-background-work-files.js +99 -0
  197. package/src/generators/slice/built-in-plan-data-deletion-files.js +2 -830
  198. package/src/generators/slice/built-in-plan-data-export-infrastructure-files.js +5 -31
  199. package/src/generators/slice/built-in-plan-data-files.d.ts +1 -1
  200. package/src/generators/slice/built-in-plan-data-files.js +163 -1196
  201. package/src/generators/slice/built-in-plan-data-lifecycle-files.js +53 -28
  202. package/src/generators/slice/built-in-plan-data-lifecycle-runtime-files.js +37 -2
  203. package/src/generators/slice/built-in-plan-data-retention-job-files.js +7 -2443
  204. package/src/generators/slice/built-in-plan-data-retention-launcher-files.js +50 -8
  205. package/src/generators/slice/built-in-plan-data-rights-files.js +6 -4482
  206. package/src/generators/slice/built-in-plan-database-lifecycle-files.js +44 -25
  207. package/src/generators/slice/built-in-plan-database-retirement-rehearsal-files.js +20 -1
  208. package/src/generators/slice/built-in-plan-foundation-files.d.ts +2 -1
  209. package/src/generators/slice/built-in-plan-foundation-files.js +39 -18
  210. package/src/generators/slice/built-in-plan-frontend-files.js +7 -3
  211. package/src/generators/slice/built-in-plan-infrastructure-files.d.ts +1 -1
  212. package/src/generators/slice/built-in-plan-infrastructure-files.js +933 -729
  213. package/src/generators/slice/built-in-plan-infrastructure-safety-files.js +67 -11
  214. package/src/generators/slice/built-in-plan-key-value-cleanup.d.ts +4 -0
  215. package/src/generators/slice/built-in-plan-key-value-cleanup.js +100 -0
  216. package/src/generators/slice/built-in-plan-key-value-consumer.d.ts +4 -0
  217. package/src/generators/slice/built-in-plan-key-value-consumer.js +151 -0
  218. package/src/generators/slice/built-in-plan-key-value-deletion.d.ts +2 -0
  219. package/src/generators/slice/built-in-plan-key-value-deletion.js +377 -0
  220. package/src/generators/slice/built-in-plan-key-value-delivery.d.ts +2 -0
  221. package/src/generators/slice/built-in-plan-key-value-delivery.js +137 -0
  222. package/src/generators/slice/built-in-plan-key-value-export.d.ts +2 -0
  223. package/src/generators/slice/built-in-plan-key-value-export.js +595 -0
  224. package/src/generators/slice/built-in-plan-key-value-files.d.ts +2 -0
  225. package/src/generators/slice/built-in-plan-key-value-files.js +31 -0
  226. package/src/generators/slice/built-in-plan-key-value-holds.d.ts +2 -0
  227. package/src/generators/slice/built-in-plan-key-value-holds.js +189 -0
  228. package/src/generators/slice/built-in-plan-key-value-infrastructure.d.ts +2 -0
  229. package/src/generators/slice/built-in-plan-key-value-infrastructure.js +102 -0
  230. package/src/generators/slice/built-in-plan-key-value-performance.d.ts +5 -0
  231. package/src/generators/slice/built-in-plan-key-value-performance.js +530 -0
  232. package/src/generators/slice/built-in-plan-key-value-provider.d.ts +4 -0
  233. package/src/generators/slice/built-in-plan-key-value-provider.js +366 -0
  234. package/src/generators/slice/built-in-plan-key-value-records.d.ts +3 -0
  235. package/src/generators/slice/built-in-plan-key-value-records.js +232 -0
  236. package/src/generators/slice/built-in-plan-key-value-retention-infrastructure.d.ts +2 -0
  237. package/src/generators/slice/built-in-plan-key-value-retention-infrastructure.js +103 -0
  238. package/src/generators/slice/built-in-plan-key-value-retention-job-files.d.ts +2 -0
  239. package/src/generators/slice/built-in-plan-key-value-retention-job-files.js +160 -0
  240. package/src/generators/slice/built-in-plan-key-value-retention-maintenance.d.ts +2 -0
  241. package/src/generators/slice/built-in-plan-key-value-retention-maintenance.js +329 -0
  242. package/src/generators/slice/built-in-plan-key-value-retention-worker.d.ts +2 -0
  243. package/src/generators/slice/built-in-plan-key-value-retention-worker.js +477 -0
  244. package/src/generators/slice/built-in-plan-key-value-retention.d.ts +2 -0
  245. package/src/generators/slice/built-in-plan-key-value-retention.js +184 -0
  246. package/src/generators/slice/built-in-plan-metadata-files.d.ts +1 -1
  247. package/src/generators/slice/built-in-plan-metadata-files.js +157 -28
  248. package/src/generators/slice/built-in-plan-native-runtime-authority-files.d.ts +2 -0
  249. package/src/generators/slice/built-in-plan-native-runtime-authority-files.js +324 -0
  250. package/src/generators/slice/built-in-plan-operations-files.d.ts +1 -1
  251. package/src/generators/slice/built-in-plan-operations-files.js +585 -97
  252. package/src/generators/slice/built-in-plan-performance-files.d.ts +2 -1
  253. package/src/generators/slice/built-in-plan-performance-files.js +220 -1074
  254. package/src/generators/slice/built-in-plan-project-files.js +141 -135
  255. package/src/generators/slice/built-in-plan-release-files.d.ts +1 -1
  256. package/src/generators/slice/built-in-plan-release-files.js +310 -70
  257. package/src/generators/slice/built-in-plan-reliability-files.d.ts +1 -1
  258. package/src/generators/slice/built-in-plan-reliability-files.js +279 -43
  259. package/src/generators/slice/built-in-plan-secret-runtime-files.d.ts +1 -1
  260. package/src/generators/slice/built-in-plan-secret-runtime-files.js +87 -76
  261. package/src/generators/slice/built-in-plan-shared.d.ts +4 -2
  262. package/src/generators/slice/built-in-plan-shared.js +41 -19
  263. package/src/generators/slice/built-in-plan-sql-events.d.ts +2 -0
  264. package/src/generators/slice/built-in-plan-sql-events.js +276 -0
  265. package/src/generators/slice/built-in-plan-testing-files.d.ts +1 -1
  266. package/src/generators/slice/built-in-plan-testing-files.js +304 -172
  267. package/src/generators/slice/built-in-plan-testing-persistence-files.d.ts +2 -0
  268. package/src/generators/slice/built-in-plan-testing-persistence-files.js +178 -0
  269. package/src/generators/slice/built-in-plan-thin-release-files.d.ts +11 -0
  270. package/src/generators/slice/built-in-plan-thin-release-files.js +162 -0
  271. package/src/generators/slice/built-in-plan-verification-files.js +76 -2
  272. package/src/generators/slice/built-in-plan-web-files.d.ts +1 -1
  273. package/src/generators/slice/built-in-plan-web-files.js +223 -16
  274. package/src/generators/slice/built-in-plan-workload-role-files.d.ts +1 -1
  275. package/src/generators/slice/built-in-plan-workload-role-files.js +143 -279
  276. package/src/generators/slice/built-in-plan.d.ts +11 -1
  277. package/src/generators/slice/built-in-plan.js +80 -29
  278. package/src/generators/slice/data-policy-review-seed.d.ts +2 -0
  279. package/src/generators/slice/data-policy-review-seed.js +22 -0
  280. package/src/generators/slice/deployed-smoke-readiness.d.ts +2 -0
  281. package/src/generators/slice/deployed-smoke-readiness.js +214 -0
  282. package/src/generators/slice/local-source-identity.d.ts +2 -0
  283. package/src/generators/slice/local-source-identity.js +31 -0
  284. package/src/generators/slice/persistence-contract.d.ts +14 -0
  285. package/src/generators/slice/persistence-contract.js +32 -0
  286. package/src/generators/slice/slice.js +61 -50
  287. package/src/generators/sql/schema.d.ts +7 -0
  288. package/src/generators/sql/schema.json +38 -0
  289. package/src/generators/sql/sql.d.ts +6 -0
  290. package/src/generators/sql/sql.js +53 -0
  291. package/src/generators/upgrade/sql-seeded-reconciliation.d.ts +2 -0
  292. package/src/generators/upgrade/sql-seeded-reconciliation.js +170 -0
  293. package/src/generators/upgrade/upgrade.d.ts +15 -1
  294. package/src/generators/upgrade/upgrade.js +435 -44
  295. package/src/generators/workload/performance-registration.d.ts +1 -0
  296. package/src/generators/workload/performance-registration.js +9 -11
  297. package/src/generators/workload/service-performance-files.d.ts +1 -0
  298. package/src/generators/workload/service-performance-files.js +27 -7
  299. package/src/generators/workload/workload.d.ts +1 -1
  300. package/src/generators/workload/workload.js +105 -29
  301. package/src/git-policy/action-pins.d.ts +5 -0
  302. package/src/git-policy/action-pins.js +5 -0
  303. package/src/git-policy/github-bootstrap.d.ts +39 -8
  304. package/src/git-policy/github-bootstrap.js +329 -175
  305. package/src/git-policy/routing.d.ts +7 -1
  306. package/src/git-policy/routing.js +62 -1
  307. package/src/index.d.ts +1 -0
  308. package/src/index.js +1 -0
  309. package/src/infrastructure-change/local-capability.d.ts +73 -0
  310. package/src/infrastructure-change/local-capability.js +114 -0
  311. package/src/infrastructure-change/model.d.ts +25 -4
  312. package/src/infrastructure-change/model.js +1 -0
  313. package/src/infrastructure-change/normalize.d.ts +4 -0
  314. package/src/infrastructure-change/normalize.js +47 -7
  315. package/src/infrastructure-change/plan.d.ts +30 -3
  316. package/src/infrastructure-change/plan.js +323 -13
  317. package/src/infrastructure-change/risk.d.ts +3 -1
  318. package/src/infrastructure-change/risk.js +96 -16
  319. package/src/local-dev/browser-verification.d.ts +37 -0
  320. package/src/local-dev/browser-verification.js +281 -0
  321. package/src/local-dev/contracts.d.ts +10 -1
  322. package/src/local-dev/contracts.js +114 -13
  323. package/src/local-dev/dev-command-core.js +24 -5
  324. package/src/local-dev/docker-credential-health.d.ts +14 -0
  325. package/src/local-dev/docker-credential-health.js +94 -0
  326. package/src/local-dev/environment-inspector.d.ts +7 -0
  327. package/src/local-dev/environment-inspector.js +42 -15
  328. package/src/local-dev/environment-state.js +2 -2
  329. package/src/local-dev/errors.d.ts +1 -1
  330. package/src/local-dev/errors.js +2 -0
  331. package/src/local-dev/fleet-client.js +2 -2
  332. package/src/local-dev/fleet-configuration.js +57 -25
  333. package/src/local-dev/fleet-inventory.js +46 -18
  334. package/src/local-dev/fleet-ipc.d.ts +7 -0
  335. package/src/local-dev/fleet-ipc.js +14 -2
  336. package/src/local-dev/fleet-launcher.d.ts +6 -0
  337. package/src/local-dev/fleet-launcher.js +14 -2
  338. package/src/local-dev/fleet-logs.js +50 -22
  339. package/src/local-dev/fleet-process.d.ts +20 -1
  340. package/src/local-dev/fleet-process.js +197 -41
  341. package/src/local-dev/fleet-runner-evidence.d.ts +5 -0
  342. package/src/local-dev/fleet-runner-evidence.js +56 -0
  343. package/src/local-dev/fleet-runtime.js +268 -229
  344. package/src/local-dev/fleet-session.d.ts +2 -0
  345. package/src/local-dev/fleet-session.js +28 -11
  346. package/src/local-dev/fleet-slice-runner-daemon.js +4 -0
  347. package/src/local-dev/fleet-state.d.ts +5 -2
  348. package/src/local-dev/fleet-state.js +25 -4
  349. package/src/local-dev/fleet-supervisor.d.ts +3 -0
  350. package/src/local-dev/fleet-supervisor.js +66 -11
  351. package/src/local-dev/fleet-web-start.d.ts +12 -0
  352. package/src/local-dev/fleet-web-start.js +48 -0
  353. package/src/local-dev/identity.js +3 -14
  354. package/src/local-dev/index.d.ts +1 -0
  355. package/src/local-dev/index.js +1 -0
  356. package/src/local-dev/native-completion.d.ts +9 -0
  357. package/src/local-dev/native-completion.js +130 -0
  358. package/src/local-dev/nx-workspace.d.ts +4 -2
  359. package/src/local-dev/nx-workspace.js +11 -6
  360. package/src/local-dev/personal-stage.d.ts +4 -5
  361. package/src/local-dev/personal-stage.js +6 -54
  362. package/src/local-dev/port-claims.d.ts +13 -0
  363. package/src/local-dev/port-claims.js +46 -0
  364. package/src/local-dev/ports.d.ts +14 -2
  365. package/src/local-dev/ports.js +78 -3
  366. package/src/local-dev/private-state.d.ts +11 -0
  367. package/src/local-dev/private-state.js +146 -40
  368. package/src/local-dev/process-group.d.ts +4 -0
  369. package/src/local-dev/process-group.js +29 -0
  370. package/src/local-dev/process-identity.d.ts +8 -0
  371. package/src/local-dev/process-identity.js +31 -1
  372. package/src/local-dev/proxy.js +11 -12
  373. package/src/local-dev/runtime-paths.js +1 -1
  374. package/src/local-dev/slice-claim.js +1 -1
  375. package/src/local-dev/slice-runner.d.ts +15 -6
  376. package/src/local-dev/slice-runner.js +189 -54
  377. package/src/local-dev/task-attachment.d.ts +17 -0
  378. package/src/local-dev/task-attachment.js +73 -0
  379. package/src/local-dev/task-authority.d.ts +10 -0
  380. package/src/local-dev/task-authority.js +27 -0
  381. package/src/local-dev/task-identity.d.ts +33 -0
  382. package/src/local-dev/task-identity.js +418 -0
  383. package/src/local-dev/task-native-writer.d.ts +18 -0
  384. package/src/local-dev/task-native-writer.js +329 -0
  385. package/src/local-dev/tls.js +8 -2
  386. package/src/local-dev/web-launch.d.ts +9 -0
  387. package/src/local-dev/web-launch.js +130 -0
  388. package/src/ownership/composition.d.ts +2 -1
  389. package/src/ownership/composition.js +1 -0
  390. package/src/performance/cost-collection.d.ts +1 -1
  391. package/src/performance/cost-collection.js +4 -2
  392. package/src/performance/cost-controls.d.ts +8 -4
  393. package/src/performance/cost-controls.js +39 -28
  394. package/src/performance/evidence.d.ts +1 -1
  395. package/src/performance/evidence.js +5 -3
  396. package/src/performance/load-safety.js +5 -3
  397. package/src/performance/model.d.ts +4 -4
  398. package/src/performance/model.js +4 -5
  399. package/src/performance/scenario.d.ts +22 -0
  400. package/src/performance/scenario.js +100 -92
  401. package/src/release/index.d.ts +0 -2
  402. package/src/release/index.js +0 -2
  403. package/src/release/source-authority.d.ts +5 -5
  404. package/src/release/source-authority.js +141 -124
  405. package/src/repository-policy/aurora-preflight.d.ts +1 -1
  406. package/src/repository-policy/aurora-preflight.js +4 -1
  407. package/src/repository-policy/capacity-migration.d.ts +3 -0
  408. package/src/repository-policy/capacity-migration.js +21 -0
  409. package/src/repository-policy/deployment-plan.d.ts +4 -1
  410. package/src/repository-policy/deployment-plan.js +9 -0
  411. package/src/repository-policy/foundation-profile.js +10 -1
  412. package/src/repository-policy/policy.d.ts +57 -3
  413. package/src/repository-policy/policy.js +497 -11
  414. package/src/schemas/blueprint-v1.json +5 -4
  415. package/src/schemas/foundation-requirement-v1.json +5 -4
  416. package/src/schemas/performance-policy-v1.json +3 -3
  417. package/src/schemas/repository-policy-v1.json +130 -6
  418. package/src/schemas/testing-evidence-result-v1.json +1 -1
  419. package/src/secrets/adapters.d.ts +7 -2
  420. package/src/secrets/adapters.js +34 -3
  421. package/src/secrets/aws-control-plane.d.ts +10 -1
  422. package/src/secrets/aws-control-plane.js +67 -0
  423. package/src/secrets/generated-aws-control-plane.d.ts +23 -0
  424. package/src/secrets/generated-aws-control-plane.js +259 -0
  425. package/src/secrets/metadata.js +38 -10
  426. package/src/secrets/model.d.ts +8 -1
  427. package/src/secrets/model.js +24 -3
  428. package/src/secrets/operator.d.ts +91 -3
  429. package/src/secrets/operator.js +463 -13
  430. package/src/secrets/registry.d.ts +2 -1
  431. package/src/secrets/registry.js +18 -1
  432. package/src/secrets/verification.js +9 -1
  433. package/src/secrets/workload-access.js +10 -2
  434. package/src/sst-environment-source-format.d.ts +6 -0
  435. package/src/sst-environment-source-format.js +199 -0
  436. package/src/supply-chain/action-pins.js +61 -7
  437. package/src/supply-chain/blueprint-trust-git.js +85 -24
  438. package/src/supply-chain/blueprint-verification-authority.js +55 -3
  439. package/src/supply-chain/build-output-bounds.d.ts +2 -0
  440. package/src/supply-chain/build-output-bounds.js +12 -0
  441. package/src/supply-chain/build-output.d.ts +7 -1
  442. package/src/supply-chain/build-output.js +177 -24
  443. package/src/supply-chain/ecr-scanning.d.ts +5 -3
  444. package/src/supply-chain/ecr-scanning.js +11 -7
  445. package/src/supply-chain/ecs-deployed-image.d.ts +5 -3
  446. package/src/supply-chain/ecs-deployed-image.js +6 -4
  447. package/src/supply-chain/generated-blueprint-trust-command.js +18 -1
  448. package/src/supply-chain/generated-command.js +1 -0
  449. package/src/supply-chain/generated-container-release-command.js +1285 -124
  450. package/src/supply-chain/generated-ecr-collector-command.d.ts +0 -1
  451. package/src/supply-chain/generated-ecr-collector-command.js +200 -93
  452. package/src/supply-chain/generated-files.js +25 -14
  453. package/src/supply-chain/generated-scanner-command.js +2 -1
  454. package/src/supply-chain/index.d.ts +1 -1
  455. package/src/supply-chain/index.js +2 -1
  456. package/src/supply-chain/sbom.d.ts +1 -1
  457. package/src/supply-chain/sbom.js +30 -5
  458. package/src/supply-chain/scanner.js +3 -0
  459. package/src/testing-evidence/fixture-scope.d.ts +2 -1
  460. package/src/testing-evidence/fixture-scope.js +4 -2
  461. package/src/testing-evidence/result.d.ts +2 -1
  462. package/src/testing-evidence/result.js +18 -4
  463. package/src/workload-roles/account-baseline.d.ts +11 -0
  464. package/src/workload-roles/account-baseline.js +253 -16
  465. package/src/workload-roles/authority.d.ts +3 -1
  466. package/src/workload-roles/authority.js +48 -2
  467. package/src/workload-roles/aws-s3-state.js +27 -0
  468. package/src/workload-roles/boundary.d.ts +2 -1
  469. package/src/workload-roles/boundary.js +9 -2
  470. package/src/workload-roles/handler.js +52 -24
  471. package/src/workload-roles/index.d.ts +10 -17
  472. package/src/workload-roles/index.js +5 -18
  473. package/src/workload-roles/injection.js +1 -1
  474. package/src/workload-roles/provisioner.js +12 -4
  475. package/src/workload-roles/registration.d.ts +1 -0
  476. package/src/workload-roles/registration.js +5 -0
  477. package/src/workload-roles/stage-boundary.js +1 -1
  478. package/src/workload-roles/standard-slice.js +3 -3
  479. package/src/workspace-deployment/affected-lifecycle.d.ts +53 -0
  480. package/src/workspace-deployment/affected-lifecycle.js +84 -0
  481. package/src/workspace-deployment/identity.d.ts +3 -0
  482. package/src/workspace-deployment/identity.js +22 -0
  483. package/src/workspace-deployment/index.d.ts +4 -0
  484. package/src/workspace-deployment/index.js +8 -0
  485. package/src/workspace-deployment/migration-evidence.d.ts +29 -0
  486. package/src/workspace-deployment/migration-evidence.js +83 -0
  487. package/src/workspace-deployment/migration.d.ts +27 -0
  488. package/src/workspace-deployment/migration.js +109 -0
  489. package/src/workspace-deployment/ownership.d.ts +81 -0
  490. package/src/workspace-deployment/ownership.js +381 -0
  491. package/src/api-contract/baseline-update.js.map +0 -1
  492. package/src/api-contract/compatibility.js.map +0 -1
  493. package/src/api-contract/contract-graph.js.map +0 -1
  494. package/src/api-contract/evidence.js.map +0 -1
  495. package/src/api-contract/index.js.map +0 -1
  496. package/src/api-contract/model.fixtures.d.ts +0 -2
  497. package/src/api-contract/model.fixtures.js +0 -81
  498. package/src/api-contract/model.fixtures.js.map +0 -1
  499. package/src/api-contract/model.js.map +0 -1
  500. package/src/api-contract/rollout.js.map +0 -1
  501. package/src/authorization/compatibility.js.map +0 -1
  502. package/src/authorization/index.js.map +0 -1
  503. package/src/blueprints/execution.js.map +0 -1
  504. package/src/blueprints/external-sandbox.js.map +0 -1
  505. package/src/blueprints/index.js.map +0 -1
  506. package/src/blueprints/inputs.js.map +0 -1
  507. package/src/blueprints/manifest.js.map +0 -1
  508. package/src/blueprints/resolver.js.map +0 -1
  509. package/src/blueprints/transitions.js.map +0 -1
  510. package/src/cli/agent-command.js.map +0 -1
  511. package/src/cli/agent-runtime.js.map +0 -1
  512. package/src/cli/blueprint-authority.js.map +0 -1
  513. package/src/cli/blueprint-command.js.map +0 -1
  514. package/src/cli/bootstrap.js.map +0 -1
  515. package/src/cli/dev-command.js.map +0 -1
  516. package/src/cli/main.js.map +0 -1
  517. package/src/cli/output.js.map +0 -1
  518. package/src/cli/personal-secret-runtime.js.map +0 -1
  519. package/src/cli/secret-terminal.js.map +0 -1
  520. package/src/container-images.js.map +0 -1
  521. package/src/data-lifecycle/index.js.map +0 -1
  522. package/src/data-lifecycle/model.fixtures.d.ts +0 -2
  523. package/src/data-lifecycle/model.fixtures.js +0 -244
  524. package/src/data-lifecycle/model.fixtures.js.map +0 -1
  525. package/src/data-lifecycle/model.js.map +0 -1
  526. package/src/data-lifecycle/sensitive-artifacts.js.map +0 -1
  527. package/src/data-lifecycle/validation.js.map +0 -1
  528. package/src/database-retirement/contract.js.map +0 -1
  529. package/src/database-retirement/index.js.map +0 -1
  530. package/src/deterministic-order.js.map +0 -1
  531. package/src/developer-agent/context.js.map +0 -1
  532. package/src/developer-agent/generator-planning.js.map +0 -1
  533. package/src/developer-agent/generator-preview.js.map +0 -1
  534. package/src/developer-agent/git-inspection.js.map +0 -1
  535. package/src/developer-agent/handoff.js.map +0 -1
  536. package/src/developer-agent/index.js.map +0 -1
  537. package/src/developer-agent/model.js.map +0 -1
  538. package/src/developer-agent/operation-registry.js.map +0 -1
  539. package/src/developer-agent/ordering.js.map +0 -1
  540. package/src/developer-agent/planning.js.map +0 -1
  541. package/src/developer-agent/preview-changes.js.map +0 -1
  542. package/src/developer-agent/preview-source.js.map +0 -1
  543. package/src/developer-agent/validation.js.map +0 -1
  544. package/src/developer-agent/workspace-inspection.js.map +0 -1
  545. package/src/developer-agent/workspace-path.js.map +0 -1
  546. package/src/executors/blueprint-trust/blueprint-trust.js.map +0 -1
  547. package/src/executors/blueprint-verification/blueprint-verification.js.map +0 -1
  548. package/src/executors/deploy/deploy.js.map +0 -1
  549. package/src/executors/deployment-plan/deployment-plan.js.map +0 -1
  550. package/src/executors/dev/dev.js.map +0 -1
  551. package/src/executors/dev-doctor/dev-doctor.js.map +0 -1
  552. package/src/executors/dev-setup/dev-setup.js.map +0 -1
  553. package/src/executors/developer-context/developer-context.js.map +0 -1
  554. package/src/executors/fleet/fleet.js.map +0 -1
  555. package/src/executors/foundation-preflight/foundation-preflight.js.map +0 -1
  556. package/src/executors/foundation-profile-preflight/foundation-profile-preflight.js.map +0 -1
  557. package/src/executors/foundation-remove-preflight/foundation-remove-preflight.js.map +0 -1
  558. package/src/executors/github-bootstrap/github-bootstrap.js.map +0 -1
  559. package/src/executors/infrastructure-plan/infrastructure-plan.js.map +0 -1
  560. package/src/executors/refresh/refresh.js.map +0 -1
  561. package/src/executors/remove/remove.js.map +0 -1
  562. package/src/executors/secret-guardrails/secret-guardrails.js.map +0 -1
  563. package/src/executors/secret-metadata/secret-metadata.js.map +0 -1
  564. package/src/executors/sst-lifecycle/logging-preflight.js.map +0 -1
  565. package/src/executors/sst-lifecycle/run-sst-command.js.map +0 -1
  566. package/src/executors/sst-lifecycle/secret-preflight.js.map +0 -1
  567. package/src/executors/unlock/unlock.js.map +0 -1
  568. package/src/executors/work-markers/work-markers.js.map +0 -1
  569. package/src/foundation/discovery.js.map +0 -1
  570. package/src/foundation/index.js.map +0 -1
  571. package/src/foundation/local-database-contract.js.map +0 -1
  572. package/src/foundation/logging-preflight.js.map +0 -1
  573. package/src/foundation/logging.js.map +0 -1
  574. package/src/foundation/preflight.js.map +0 -1
  575. package/src/foundation/provision-database-bindings.js.map +0 -1
  576. package/src/foundation/provision-logical-database.js.map +0 -1
  577. package/src/foundation/requirements.js.map +0 -1
  578. package/src/foundation/router-origin-proof.js.map +0 -1
  579. package/src/foundation/stages.js.map +0 -1
  580. package/src/frontend-security/index.js.map +0 -1
  581. package/src/frontend-security/security-headers.js.map +0 -1
  582. package/src/generators/feature-registry.js.map +0 -1
  583. package/src/generators/function/function.js.map +0 -1
  584. package/src/generators/job/job.js.map +0 -1
  585. package/src/generators/logging-registry.js.map +0 -1
  586. package/src/generators/post-generation-install.js.map +0 -1
  587. package/src/generators/preset/generator.js.map +0 -1
  588. package/src/generators/preset/tool-versions.js.map +0 -1
  589. package/src/generators/preset/workspace-files-developer-agent.js.map +0 -1
  590. package/src/generators/preset/workspace-files-foundation-workload-roles.js.map +0 -1
  591. package/src/generators/preset/workspace-files-performance-cost.js.map +0 -1
  592. package/src/generators/preset/workspace-files-performance-load.js.map +0 -1
  593. package/src/generators/preset/workspace-files-workload-role-baseline.js.map +0 -1
  594. package/src/generators/preset/workspace-files.js.map +0 -1
  595. package/src/generators/repository-policy/repository-policy.js.map +0 -1
  596. package/src/generators/service/service.js.map +0 -1
  597. package/src/generators/slice/built-in-plan-api-contract-files.js.map +0 -1
  598. package/src/generators/slice/built-in-plan-api-files.js.map +0 -1
  599. package/src/generators/slice/built-in-plan-async-work-files.js.map +0 -1
  600. package/src/generators/slice/built-in-plan-authorization-files.js.map +0 -1
  601. package/src/generators/slice/built-in-plan-authorization-infrastructure-files.js.map +0 -1
  602. package/src/generators/slice/built-in-plan-data-deletion-files.js.map +0 -1
  603. package/src/generators/slice/built-in-plan-data-export-infrastructure-files.js.map +0 -1
  604. package/src/generators/slice/built-in-plan-data-files.js.map +0 -1
  605. package/src/generators/slice/built-in-plan-data-lifecycle-files.js.map +0 -1
  606. package/src/generators/slice/built-in-plan-data-lifecycle-runtime-files.js.map +0 -1
  607. package/src/generators/slice/built-in-plan-data-retention-job-files.js.map +0 -1
  608. package/src/generators/slice/built-in-plan-data-retention-launcher-files.js.map +0 -1
  609. package/src/generators/slice/built-in-plan-data-rights-files.js.map +0 -1
  610. package/src/generators/slice/built-in-plan-database-lifecycle-files.js.map +0 -1
  611. package/src/generators/slice/built-in-plan-database-retirement-rehearsal-files.js.map +0 -1
  612. package/src/generators/slice/built-in-plan-foundation-files.js.map +0 -1
  613. package/src/generators/slice/built-in-plan-frontend-files.js.map +0 -1
  614. package/src/generators/slice/built-in-plan-infrastructure-files.js.map +0 -1
  615. package/src/generators/slice/built-in-plan-infrastructure-safety-files.js.map +0 -1
  616. package/src/generators/slice/built-in-plan-metadata-files.js.map +0 -1
  617. package/src/generators/slice/built-in-plan-operations-files.js.map +0 -1
  618. package/src/generators/slice/built-in-plan-performance-files.js.map +0 -1
  619. package/src/generators/slice/built-in-plan-project-files.js.map +0 -1
  620. package/src/generators/slice/built-in-plan-release-files.js.map +0 -1
  621. package/src/generators/slice/built-in-plan-reliability-files.js.map +0 -1
  622. package/src/generators/slice/built-in-plan-secret-runtime-files.js.map +0 -1
  623. package/src/generators/slice/built-in-plan-shared.js.map +0 -1
  624. package/src/generators/slice/built-in-plan-testing-files.js.map +0 -1
  625. package/src/generators/slice/built-in-plan-verification-files.js.map +0 -1
  626. package/src/generators/slice/built-in-plan-web-files.js.map +0 -1
  627. package/src/generators/slice/built-in-plan-workload-role-files.js.map +0 -1
  628. package/src/generators/slice/built-in-plan.js.map +0 -1
  629. package/src/generators/slice/slice.js.map +0 -1
  630. package/src/generators/upgrade/upgrade.js.map +0 -1
  631. package/src/generators/workload/frozen-container.js.map +0 -1
  632. package/src/generators/workload/performance-registration.js.map +0 -1
  633. package/src/generators/workload/reliability-registration.js.map +0 -1
  634. package/src/generators/workload/service-performance-files.js.map +0 -1
  635. package/src/generators/workload/workload.js.map +0 -1
  636. package/src/git-policy/action-pins.js.map +0 -1
  637. package/src/git-policy/github-bootstrap.js.map +0 -1
  638. package/src/git-policy/index.js.map +0 -1
  639. package/src/git-policy/routing.js.map +0 -1
  640. package/src/index.js.map +0 -1
  641. package/src/infrastructure-change/convergence.js.map +0 -1
  642. package/src/infrastructure-change/drift.js.map +0 -1
  643. package/src/infrastructure-change/index.js.map +0 -1
  644. package/src/infrastructure-change/model.js.map +0 -1
  645. package/src/infrastructure-change/normalize.js.map +0 -1
  646. package/src/infrastructure-change/plan.js.map +0 -1
  647. package/src/infrastructure-change/risk.js.map +0 -1
  648. package/src/infrastructure-change/summary.js.map +0 -1
  649. package/src/local-dev/context-discovery.js.map +0 -1
  650. package/src/local-dev/contract-validation.js.map +0 -1
  651. package/src/local-dev/contracts.js.map +0 -1
  652. package/src/local-dev/control-contract.js.map +0 -1
  653. package/src/local-dev/dev-command-core.js.map +0 -1
  654. package/src/local-dev/effects.js.map +0 -1
  655. package/src/local-dev/environment-inspector.js.map +0 -1
  656. package/src/local-dev/environment-state.js.map +0 -1
  657. package/src/local-dev/errors.js.map +0 -1
  658. package/src/local-dev/fleet-client.js.map +0 -1
  659. package/src/local-dev/fleet-configuration.js.map +0 -1
  660. package/src/local-dev/fleet-inventory.js.map +0 -1
  661. package/src/local-dev/fleet-ipc.js.map +0 -1
  662. package/src/local-dev/fleet-launcher.js.map +0 -1
  663. package/src/local-dev/fleet-lock.js.map +0 -1
  664. package/src/local-dev/fleet-logs.js.map +0 -1
  665. package/src/local-dev/fleet-process.js.map +0 -1
  666. package/src/local-dev/fleet-runtime.js.map +0 -1
  667. package/src/local-dev/fleet-session.js.map +0 -1
  668. package/src/local-dev/fleet-slice-runner-daemon.js.map +0 -1
  669. package/src/local-dev/fleet-startup.js.map +0 -1
  670. package/src/local-dev/fleet-state.js.map +0 -1
  671. package/src/local-dev/fleet-supervisor-daemon.js.map +0 -1
  672. package/src/local-dev/fleet-supervisor.js.map +0 -1
  673. package/src/local-dev/gateway-daemon.js.map +0 -1
  674. package/src/local-dev/gateway.js.map +0 -1
  675. package/src/local-dev/identity.js.map +0 -1
  676. package/src/local-dev/index.js.map +0 -1
  677. package/src/local-dev/local-context.js.map +0 -1
  678. package/src/local-dev/nx-workspace.js.map +0 -1
  679. package/src/local-dev/personal-secret-ledger.js.map +0 -1
  680. package/src/local-dev/personal-stage.js.map +0 -1
  681. package/src/local-dev/portless.js.map +0 -1
  682. package/src/local-dev/ports.js.map +0 -1
  683. package/src/local-dev/private-state.js.map +0 -1
  684. package/src/local-dev/process-identity.js.map +0 -1
  685. package/src/local-dev/proxy.js.map +0 -1
  686. package/src/local-dev/runtime-paths.js.map +0 -1
  687. package/src/local-dev/secret-slice-claim.js.map +0 -1
  688. package/src/local-dev/slice-claim.js.map +0 -1
  689. package/src/local-dev/slice-runner.js.map +0 -1
  690. package/src/local-dev/tls.js.map +0 -1
  691. package/src/ownership/composition.js.map +0 -1
  692. package/src/ownership/foundation-record.js.map +0 -1
  693. package/src/ownership/hash.js.map +0 -1
  694. package/src/ownership/index.js.map +0 -1
  695. package/src/ownership/json-ownership.js.map +0 -1
  696. package/src/ownership/record.js.map +0 -1
  697. package/src/ownership/workspace-record.js.map +0 -1
  698. package/src/performance/cost-collection.js.map +0 -1
  699. package/src/performance/cost-controls.js.map +0 -1
  700. package/src/performance/evidence.js.map +0 -1
  701. package/src/performance/index.js.map +0 -1
  702. package/src/performance/load-safety.js.map +0 -1
  703. package/src/performance/model.js.map +0 -1
  704. package/src/performance/pr-artifact-budget.js.map +0 -1
  705. package/src/performance/runner.js.map +0 -1
  706. package/src/performance/scenario-endpoint.js.map +0 -1
  707. package/src/performance/scenario.js.map +0 -1
  708. package/src/performance/test-fixtures.d.ts +0 -7
  709. package/src/performance/test-fixtures.js +0 -169
  710. package/src/performance/test-fixtures.js.map +0 -1
  711. package/src/performance/unit-cost.js.map +0 -1
  712. package/src/release/index.js.map +0 -1
  713. package/src/release/lifecycle.js.map +0 -1
  714. package/src/release/manifest.js.map +0 -1
  715. package/src/release/rollout.js.map +0 -1
  716. package/src/release/source-authority.js.map +0 -1
  717. package/src/repository-policy/aurora-preflight.js.map +0 -1
  718. package/src/repository-policy/deployment-plan.js.map +0 -1
  719. package/src/repository-policy/foundation-profile.js.map +0 -1
  720. package/src/repository-policy/index.js.map +0 -1
  721. package/src/repository-policy/policy.js.map +0 -1
  722. package/src/secrets/adapters.js.map +0 -1
  723. package/src/secrets/aws-control-plane.js.map +0 -1
  724. package/src/secrets/declaration-verification.js.map +0 -1
  725. package/src/secrets/guardrails.js.map +0 -1
  726. package/src/secrets/index.js.map +0 -1
  727. package/src/secrets/metadata.js.map +0 -1
  728. package/src/secrets/model.js.map +0 -1
  729. package/src/secrets/operator.js.map +0 -1
  730. package/src/secrets/permissions.js.map +0 -1
  731. package/src/secrets/personal-command.js.map +0 -1
  732. package/src/secrets/personal-ledger.js.map +0 -1
  733. package/src/secrets/registry.js.map +0 -1
  734. package/src/secrets/verification.js.map +0 -1
  735. package/src/secrets/workload-access.js.map +0 -1
  736. package/src/supply-chain/action-pins.js.map +0 -1
  737. package/src/supply-chain/blueprint-trust-git.js.map +0 -1
  738. package/src/supply-chain/blueprint-trust-process.js.map +0 -1
  739. package/src/supply-chain/blueprint-trust.js.map +0 -1
  740. package/src/supply-chain/blueprint-verification-authority.js.map +0 -1
  741. package/src/supply-chain/blueprint-verification-execution.js.map +0 -1
  742. package/src/supply-chain/build-output.js.map +0 -1
  743. package/src/supply-chain/common.js.map +0 -1
  744. package/src/supply-chain/container-artifact-contract.js.map +0 -1
  745. package/src/supply-chain/container-release.js.map +0 -1
  746. package/src/supply-chain/ecr-applicability.js.map +0 -1
  747. package/src/supply-chain/ecr-scanning.js.map +0 -1
  748. package/src/supply-chain/ecs-deployed-image.js.map +0 -1
  749. package/src/supply-chain/errors.js.map +0 -1
  750. package/src/supply-chain/generated-blueprint-trust-command.js.map +0 -1
  751. package/src/supply-chain/generated-command.js.map +0 -1
  752. package/src/supply-chain/generated-container-release-command.js.map +0 -1
  753. package/src/supply-chain/generated-ecr-collector-command.js.map +0 -1
  754. package/src/supply-chain/generated-files.js.map +0 -1
  755. package/src/supply-chain/generated-scanner-command.js.map +0 -1
  756. package/src/supply-chain/index.js.map +0 -1
  757. package/src/supply-chain/inventory.js.map +0 -1
  758. package/src/supply-chain/license-expression.js.map +0 -1
  759. package/src/supply-chain/pnpm-toolchain-authority.js.map +0 -1
  760. package/src/supply-chain/policy.js.map +0 -1
  761. package/src/supply-chain/publication.js.map +0 -1
  762. package/src/supply-chain/sbom.js.map +0 -1
  763. package/src/supply-chain/scanner.js.map +0 -1
  764. package/src/supply-chain/vendored-source.js.map +0 -1
  765. package/src/testing-evidence/fixture-scope.js.map +0 -1
  766. package/src/testing-evidence/index.js.map +0 -1
  767. package/src/testing-evidence/quarantine.js.map +0 -1
  768. package/src/testing-evidence/result.js.map +0 -1
  769. package/src/testing-evidence/runner-adapter.js.map +0 -1
  770. package/src/testing-evidence/sensitive-artifacts.js.map +0 -1
  771. package/src/validation.js.map +0 -1
  772. package/src/work-markers/index.js.map +0 -1
  773. package/src/work-markers/work-markers.js.map +0 -1
  774. package/src/workload-roles/account-baseline.js.map +0 -1
  775. package/src/workload-roles/authority.js.map +0 -1
  776. package/src/workload-roles/aws-adapter.js.map +0 -1
  777. package/src/workload-roles/aws-internal-handler.js.map +0 -1
  778. package/src/workload-roles/aws-s3-state.js.map +0 -1
  779. package/src/workload-roles/aws-sdk-control-plane.js.map +0 -1
  780. package/src/workload-roles/boundary.js.map +0 -1
  781. package/src/workload-roles/convergence.js.map +0 -1
  782. package/src/workload-roles/effective-policy.js.map +0 -1
  783. package/src/workload-roles/handler.js.map +0 -1
  784. package/src/workload-roles/history.js.map +0 -1
  785. package/src/workload-roles/index.js.map +0 -1
  786. package/src/workload-roles/injection.js.map +0 -1
  787. package/src/workload-roles/manifest.js.map +0 -1
  788. package/src/workload-roles/provisioner.js.map +0 -1
  789. package/src/workload-roles/registration.js.map +0 -1
  790. package/src/workload-roles/stage-boundary.js.map +0 -1
  791. package/src/workload-roles/standard-slice.js.map +0 -1
@@ -2,180 +2,397 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.renderProductInfrastructureFiles = renderProductInfrastructureFiles;
4
4
  const built_in_plan_shared_js_1 = require("./built-in-plan-shared.js");
5
+ const built_in_plan_key_value_retention_infrastructure_js_1 = require("./built-in-plan-key-value-retention-infrastructure.js");
5
6
  /** @internal */
6
- function renderProductInfrastructureFiles(name, pascalName, workspace) {
7
+ function renderProductInfrastructureFiles(name, pascalName, workspace, protectedRuntimeStages = ['staging', 'production'], sql = false, network = sql) {
7
8
  const identifierPrefix = (0, built_in_plan_shared_js_1.lowerCamelIdentifier)(pascalName);
8
9
  const recordExportWorkflowName = (0, built_in_plan_shared_js_1.stableGeneratedName)(40, name);
9
- return {
10
- 'jobs/data-retention-maintenance-launcher.ts': `import {
11
- ECSClient,
12
- RunTaskCommand,
13
- } from '@aws-sdk/client-ecs';
14
- import { createLogger } from '@empire-builder-kit/runtime/logging';
10
+ const files = {
11
+ 'infra/event-subscriptions.ts': `/** Read the existing seeded event contract; never broaden source or stage routing. */
12
+ export function sameSliceEventDetailTypes(value: unknown): string[] {
13
+ const contract = value as {
14
+ kind?: unknown;
15
+ schemaVersion?: unknown;
16
+ slice?: unknown;
17
+ events?: unknown;
18
+ } | null;
19
+ if (
20
+ !contract || contract.kind !== 'ebk-event-contract-schema' ||
21
+ contract.schemaVersion !== 1 || contract.slice !== '${name}' ||
22
+ !Array.isArray(contract.events) || contract.events.length < 2 ||
23
+ contract.events.length > 32
24
+ ) throw new Error('Event subscriptions require the bounded seeded ${name} EventContractSchema.');
25
+ const types = new Set<string>();
26
+ for (const raw of contract.events) {
27
+ const event = raw as {
28
+ detailType?: unknown;
29
+ source?: unknown;
30
+ schemaVersion?: unknown;
31
+ data?: unknown;
32
+ } | null;
33
+ if (
34
+ !event || typeof event.detailType !== 'string' ||
35
+ !/^[A-Za-z][A-Za-z0-9._-]{0,127}$/.test(event.detailType) ||
36
+ event.source !== '${name}' || typeof event.schemaVersion !== 'number' ||
37
+ !Number.isSafeInteger(event.schemaVersion) || event.schemaVersion < 1 ||
38
+ !event.data || typeof event.data !== 'object' || Array.isArray(event.data) ||
39
+ types.has(event.detailType)
40
+ ) throw new Error('Event subscriptions require unique literal types, exact Slice sources and valid schemas.');
41
+ types.add(event.detailType);
42
+ }
43
+ if (!types.has('${pascalName}Created') || !types.has('DataLifecycleChanged'))
44
+ throw new Error('The generated event contract must retain its built-in event declarations.');
45
+ // Lifecycle consumers have their own exact source declaration and rule.
46
+ types.delete('DataLifecycleChanged');
47
+ return [...types].sort();
48
+ }
49
+ `,
50
+ 'infra/route-function-materialization.ts': `type RouteFunctionArgs = {
51
+ ebkRouteMaterialization?: unknown;
52
+ handler: string;
53
+ environment?: unknown;
54
+ link?: unknown;
55
+ permissions?: unknown;
56
+ } & Record<string, any>;
15
57
 
16
- const UUID =
17
- /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
18
- const ECS_CLUSTER_ARN =
19
- new RegExp(
20
- '^arn:(?:aws|aws-cn|aws-us-gov):ecs:[a-z0-9-]+:' +
21
- '[0-9]{12}:cluster/[A-Za-z0-9_./-]{1,255}$',
22
- );
23
- const ECS_TASK_DEFINITION_ARN =
24
- /^arn:(?:aws|aws-cn|aws-us-gov):ecs:[a-z0-9-]+:[0-9]{12}:task-definition\\/[A-Za-z0-9_-]{1,255}:[1-9][0-9]*$/;
25
- const NETWORK_ID = /^(?:sg|subnet)-[0-9a-f]{8,32}$/;
26
- const CONTAINER_NAME = /^[A-Za-z0-9_-]{1,255}$/;
58
+ type OutputFactory = (value: any) => {
59
+ apply(callback: (value: any) => any): unknown;
60
+ };
27
61
 
28
- function required(name: string): string {
29
- const value = process.env[name]?.trim();
30
- if (!value) throw new Error(name + ' is required.');
31
- return value;
32
- }
62
+ type RouteRegistrar<Result> = (
63
+ rawRoute: string,
64
+ handler: string,
65
+ args?: Record<string, any>,
66
+ ) => Result;
33
67
 
34
- function requiredArn(name: string, pattern: RegExp): string {
35
- const value = required(name);
36
- if (!pattern.test(value)) {
37
- throw new Error(name + ' is not the exact expected ECS ARN.');
68
+ export function createVpcRouteFunctionMaterializer(input: {
69
+ directHandlers: readonly string[];
70
+ foundationVpc?: unknown;
71
+ output: OutputFactory;
72
+ }) {
73
+ const directHandlerLimit = 2;
74
+ const compositionDirectHandlerLimit = 64;
75
+ const routeFunctionLimit = 23;
76
+ if (
77
+ !Array.isArray(input.directHandlers) ||
78
+ input.directHandlers.length !== directHandlerLimit ||
79
+ input.directHandlers.some(
80
+ (handler) =>
81
+ typeof handler !== 'string' ||
82
+ handler.length === 0 ||
83
+ handler.length > 256,
84
+ ) ||
85
+ new Set(input.directHandlers).size !== input.directHandlers.length
86
+ ) {
87
+ throw new Error(
88
+ 'Direct API routes require the exact bounded unique handler inventory.',
89
+ );
38
90
  }
39
- return value;
40
- }
91
+ const directHandlers = new Set(input.directHandlers);
92
+ const compositionDirectHandlers = new Set<string>();
93
+ const routeFunctions = new Map<string, RouteFunctionArgs>();
94
+ const materializedRouteFunctions = new Set<string>();
41
95
 
42
- function requiredBoolean(name: string): boolean {
43
- const value = required(name);
44
- if (value !== 'true' && value !== 'false') {
45
- throw new Error(name + ' must be exactly true or false.');
46
- }
47
- return value === 'true';
96
+ const transform = (args: any): void => {
97
+ if (
98
+ typeof args !== 'object' ||
99
+ args === null ||
100
+ Array.isArray(args) ||
101
+ typeof args.handler !== 'string' ||
102
+ args.handler.length === 0
103
+ ) {
104
+ throw new Error(
105
+ 'API route handler transforms require one literal handler path.',
106
+ );
107
+ }
108
+ const routeFunction = routeFunctions.get(args.handler);
109
+ if (!routeFunction) {
110
+ if (directHandlers.has(args.handler)) {
111
+ if (args.ebkRouteMaterialization !== undefined) {
112
+ throw new Error(
113
+ 'Built-in direct API route materialization authority is ambiguous.',
114
+ );
115
+ }
116
+ return;
117
+ }
118
+ if (
119
+ args.ebkRouteMaterialization === 'direct' &&
120
+ !compositionDirectHandlers.has(args.handler) &&
121
+ compositionDirectHandlers.size < compositionDirectHandlerLimit
122
+ ) {
123
+ delete args.ebkRouteMaterialization;
124
+ compositionDirectHandlers.add(args.handler);
125
+ return;
126
+ }
127
+ throw new Error(
128
+ 'API route handler lacks registered materialization authority.',
129
+ );
130
+ }
131
+ if (
132
+ args.ebkRouteMaterialization !== undefined ||
133
+ routeFunction.handler !== args.handler ||
134
+ materializedRouteFunctions.has(args.handler)
135
+ ) {
136
+ throw new Error(
137
+ 'VPC API route handler materialization authority is ambiguous.',
138
+ );
139
+ }
140
+
141
+ // SST 4.17.1 resolves the complete FunctionArgs definition before it
142
+ // calls this transform. Passing imported Foundation Outputs as that
143
+ // definition can therefore defer the route component itself during
144
+ // refresh. Keep the definition literal until here, then reproduce
145
+ // functionBuilder's route-over-default merge exactly.
146
+ const defaultEnvironment = args.environment;
147
+ const defaultLink = args.link;
148
+ const defaultPermissions = args.permissions;
149
+ Object.assign(args, routeFunction);
150
+ args.environment = input
151
+ .output([defaultEnvironment, routeFunction.environment])
152
+ .apply(([defaults, route]: [any, any]) => ({
153
+ ...(defaults ?? {}),
154
+ ...(route ?? {}),
155
+ }));
156
+ args.link = input
157
+ .output([defaultLink, routeFunction.link])
158
+ .apply(([defaults, route]: [any, any]) => [
159
+ ...(defaults ?? []),
160
+ ...(route ?? []),
161
+ ]);
162
+ args.permissions = input
163
+ .output([defaultPermissions, routeFunction.permissions])
164
+ .apply(([defaults, route]: [any, any]) => [
165
+ ...(defaults ?? []),
166
+ ...(route ?? []),
167
+ ]);
168
+ materializedRouteFunctions.add(args.handler);
169
+ };
170
+
171
+ const route = <Result>(
172
+ rawRoute: string,
173
+ handler: RouteFunctionArgs,
174
+ args: Record<string, any> | undefined,
175
+ register: RouteRegistrar<Result>,
176
+ ): Result => {
177
+ const handlerPath = handler.handler;
178
+ if (
179
+ typeof rawRoute !== 'string' ||
180
+ rawRoute.length === 0 ||
181
+ rawRoute.length > 512 ||
182
+ typeof handlerPath !== 'string' ||
183
+ handlerPath.length === 0 ||
184
+ handlerPath.length > 256 ||
185
+ typeof register !== 'function' ||
186
+ directHandlers.has(handlerPath) ||
187
+ routeFunctions.has(handlerPath) ||
188
+ routeFunctions.size >= routeFunctionLimit
189
+ ) {
190
+ throw new Error(
191
+ 'VPC API routes require one bounded unique literal handler registration.',
192
+ );
193
+ }
194
+ routeFunctions.set(handlerPath, {
195
+ ...handler,
196
+ handler: handlerPath,
197
+ ...(input.foundationVpc === undefined ? {} : { vpc: input.foundationVpc }),
198
+ });
199
+ try {
200
+ return register(rawRoute, handlerPath, args);
201
+ } catch (error) {
202
+ routeFunctions.delete(handlerPath);
203
+ throw error;
204
+ }
205
+ };
206
+
207
+ return Object.freeze({ route, transform });
48
208
  }
209
+ `,
210
+ 'infra/next-build-authority.ts': `import {
211
+ chmodSync,
212
+ existsSync,
213
+ lstatSync,
214
+ readdirSync,
215
+ realpathSync,
216
+ unlinkSync,
217
+ } from 'node:fs';
218
+ import { join, relative, resolve } from 'node:path';
49
219
 
50
- function requiredNetworkIds(name: string): string[] {
51
- let value: unknown;
52
- try {
53
- value = JSON.parse(required(name));
54
- } catch {
55
- throw new Error(name + ' must be one bounded JSON array.');
56
- }
220
+ const maximumEntries = 65_536;
221
+ const maximumDepth = 64;
222
+ const maximumFinderMetadataFiles = 256;
223
+ const maximumFinderMetadataBytes = 1_048_576n;
224
+
225
+ // SST 4.17.1 awaits FunctionArgs.hook.postbuild after Runtime.Build has
226
+ // materialized the exact directory that its Lambda zipper consumes. Finder
227
+ // metadata removed only from OpenNext's earlier output can be recreated or
228
+ // copied through package-local node_modules before that boundary. Sanitize the
229
+ // final derived bundle here so the bytes reviewed by the infrastructure plan
230
+ // are the bytes SST packages.
231
+ export function removeFinderMetadataFromBuiltFunction(rootPath: string): number {
232
+ const root = resolve(rootPath);
233
+ const rootIdentity = lstatSync(root, { bigint: true });
57
234
  if (
58
- !Array.isArray(value) ||
59
- value.length < 1 ||
60
- value.length > 16 ||
61
- value.some(
62
- (entry) => typeof entry !== 'string' || !NETWORK_ID.test(entry),
63
- )
235
+ !rootIdentity.isDirectory() ||
236
+ rootIdentity.isSymbolicLink() ||
237
+ realpathSync(root) !== root
64
238
  ) {
65
- throw new Error(name + ' must contain only exact AWS network IDs.');
239
+ throw new Error('The built Function root has an invalid identity.');
66
240
  }
67
- return value;
68
- }
69
241
 
70
- function requiredScheduledEventId(event: unknown): string {
71
- const id =
72
- event && typeof event === 'object' && 'id' in event
73
- ? (event as { id?: unknown }).id
74
- : undefined;
75
- if (typeof id !== 'string' || !UUID.test(id)) {
76
- throw new Error('Scheduled maintenance requires one exact event ID.');
242
+ const pendingDirectories = [{ depth: 0, path: root }];
243
+ let observedEntries = 0;
244
+ let removedFinderMetadata = 0;
245
+ while (pendingDirectories.length > 0) {
246
+ const current = pendingDirectories.pop();
247
+ if (!current) break;
248
+ if (current.depth > maximumDepth) {
249
+ throw new Error('The built Function tree exceeds its depth bound.');
250
+ }
251
+ for (const entry of readdirSync(current.path, { withFileTypes: true }).sort(
252
+ (left, right) =>
253
+ left.name < right.name ? -1 : left.name > right.name ? 1 : 0,
254
+ )) {
255
+ observedEntries += 1;
256
+ if (observedEntries > maximumEntries) {
257
+ throw new Error('The built Function tree exceeds its entry bound.');
258
+ }
259
+ const entryPath = join(current.path, entry.name);
260
+ const identity = lstatSync(entryPath, { bigint: true });
261
+ if (entry.name === '.DS_Store') {
262
+ removedFinderMetadata += 1;
263
+ if (
264
+ removedFinderMetadata > maximumFinderMetadataFiles ||
265
+ !identity.isFile() ||
266
+ identity.isSymbolicLink() ||
267
+ identity.nlink !== 1n ||
268
+ identity.size > maximumFinderMetadataBytes
269
+ ) {
270
+ throw new Error(
271
+ 'The built Function Finder metadata has an invalid identity.',
272
+ );
273
+ }
274
+ unlinkSync(entryPath);
275
+ if (existsSync(entryPath)) {
276
+ throw new Error(
277
+ 'The built Function Finder metadata was not removed.',
278
+ );
279
+ }
280
+ continue;
281
+ }
282
+ if (identity.isDirectory() && !identity.isSymbolicLink()) {
283
+ const confined = relative(root, entryPath);
284
+ if (
285
+ confined === '..' ||
286
+ confined.startsWith('../') ||
287
+ resolve(root, confined) !== entryPath
288
+ ) {
289
+ throw new Error('The built Function entry escapes its root.');
290
+ }
291
+ pendingDirectories.push({ depth: current.depth + 1, path: entryPath });
292
+ }
293
+ }
294
+ // Finder can recreate its metadata in the interval between this hook and
295
+ // SST's asynchronous glob/archive pass. On macOS, close that exact race by
296
+ // removing only the owner's write bit from each completed derived
297
+ // directory. The generated build wrapper restores it before the next
298
+ // OpenNext build. Group/other permission bits remain unchanged.
299
+ if (process.platform === 'darwin') {
300
+ const currentIdentity = lstatSync(current.path, { bigint: true });
301
+ if (
302
+ !currentIdentity.isDirectory() ||
303
+ currentIdentity.isSymbolicLink()
304
+ ) {
305
+ throw new Error('The built Function directory changed identity.');
306
+ }
307
+ chmodSync(
308
+ current.path,
309
+ Number((currentIdentity.mode & 0o7_777n) & ~0o200n),
310
+ );
311
+ if ((lstatSync(current.path, { bigint: true }).mode & 0o200n) !== 0n) {
312
+ throw new Error('The built Function directory was not sealed.');
313
+ }
314
+ if (existsSync(join(current.path, '.DS_Store'))) {
315
+ throw new Error(
316
+ 'The built Function Finder metadata raced its final seal.',
317
+ );
318
+ }
319
+ }
77
320
  }
78
- return id.toLowerCase();
321
+ return removedFinderMetadata;
322
+ }
323
+ `,
324
+ 'infra/deployment-receipt.ts': `export interface SuccessfulDeploymentReceiptExpectation {
325
+ account: string;
326
+ project: string;
327
+ region: string;
328
+ sourceCommit: string;
329
+ stage: 'development' | 'staging' | 'production';
79
330
  }
80
331
 
81
- export async function handler(event: unknown) {
82
- const stage = required('EBK_STAGE');
83
- const logger = createLogger({
84
- component: 'job-data-retention-maintenance-launcher',
85
- feature: 'data-lifecycle',
86
- operation: 'retention-maintenance-launch',
87
- slice: '${name}',
88
- stage,
89
- workspace: required('EBK_WORKSPACE'),
90
- });
91
- const correlationId = requiredScheduledEventId(event);
92
- try {
93
- const containerName = required('EBK_MAINTENANCE_CONTAINER_NAME');
94
- if (!CONTAINER_NAME.test(containerName)) {
95
- throw new Error('Maintenance container name is invalid.');
96
- }
97
- const response = await new ECSClient({}).send(
98
- new RunTaskCommand({
99
- cluster: requiredArn('EBK_MAINTENANCE_CLUSTER_ARN', ECS_CLUSTER_ARN),
100
- count: 1,
101
- enableExecuteCommand: false,
102
- launchType: 'FARGATE',
103
- networkConfiguration: {
104
- awsvpcConfiguration: {
105
- assignPublicIp:
106
- requiredBoolean('EBK_MAINTENANCE_ASSIGN_PUBLIC_IP')
107
- ? 'ENABLED'
108
- : 'DISABLED',
109
- securityGroups: requiredNetworkIds(
110
- 'EBK_MAINTENANCE_SECURITY_GROUP_IDS',
111
- ),
112
- subnets: requiredNetworkIds('EBK_MAINTENANCE_SUBNET_IDS'),
113
- },
114
- },
115
- overrides: {
116
- containerOverrides: [
117
- {
118
- command: [
119
- 'node',
120
- 'dist/jobs/data-retention/src/maintenance.js',
121
- ],
122
- environment: [
123
- {
124
- name: 'EBK_MAINTENANCE_CORRELATION_ID',
125
- value: correlationId,
126
- },
127
- ],
128
- name: containerName,
129
- },
130
- ],
131
- },
132
- taskDefinition: requiredArn(
133
- 'EBK_MAINTENANCE_TASK_DEFINITION_ARN',
134
- ECS_TASK_DEFINITION_ARN,
135
- ),
136
- }),
332
+ export function validateSuccessfulDeploymentReceipt(
333
+ value: unknown,
334
+ expected: SuccessfulDeploymentReceiptExpectation,
335
+ ) {
336
+ const receipt = value as {
337
+ account?: unknown;
338
+ authority?: unknown;
339
+ children?: Array<{
340
+ outcome?: unknown;
341
+ project?: unknown;
342
+ target?: unknown;
343
+ }>;
344
+ kind?: unknown;
345
+ migrations?: Record<string, unknown>;
346
+ outcome?: unknown;
347
+ profile?: unknown;
348
+ region?: unknown;
349
+ schemaVersion?: unknown;
350
+ smoke?: Record<string, unknown>;
351
+ source?: { clean?: unknown; commit?: unknown };
352
+ stage?: unknown;
353
+ };
354
+ const deploymentChildren = Array.isArray(receipt?.children)
355
+ ? receipt.children.filter(
356
+ ({ outcome, project, target }) =>
357
+ outcome === 'succeeded' &&
358
+ project === expected.project &&
359
+ target === 'deploy',
360
+ )
361
+ : [];
362
+ if (
363
+ receipt?.kind !== 'ebk-deployment-receipt' ||
364
+ receipt.schemaVersion !== 1 ||
365
+ receipt.outcome !== 'succeeded' ||
366
+ !['local', 'github'].includes(String(receipt.authority)) ||
367
+ !['starter', 'production-isolated', 'fully-isolated'].includes(
368
+ String(receipt.profile),
369
+ ) ||
370
+ receipt.account !== expected.account ||
371
+ receipt.region !== expected.region ||
372
+ receipt.stage !== expected.stage ||
373
+ receipt.source?.clean !== true ||
374
+ receipt.source.commit !== expected.sourceCommit ||
375
+ !Array.isArray(receipt.children) ||
376
+ receipt.children.some(({ outcome }) => outcome !== 'succeeded') ||
377
+ deploymentChildren.length !== 1 ||
378
+ receipt.migrations?.[expected.project] !== 'completed-by-deploy-target' ||
379
+ receipt.smoke?.[expected.project] !== 'passed'
380
+ ) {
381
+ throw new Error(
382
+ 'Protected performance load requires one successful exact-source native deployment receipt.',
137
383
  );
138
- if (
139
- (response.failures?.length ?? 0) !== 0 ||
140
- response.tasks?.length !== 1 ||
141
- !response.tasks[0]?.taskArn
142
- ) {
143
- throw new Error('ECS did not accept exactly one maintenance task.');
144
- }
145
- logger.info('Retention maintenance task launched', {
146
- attributes: { launchCount: 1 },
147
- outcome: 'success',
148
- });
149
- return { accepted: true };
150
- } catch (error) {
151
- logger.error('Retention maintenance task launch failed', {
152
- attributes: {
153
- errorCode:
154
- error instanceof Error
155
- ? error.name.replace(/[^A-Za-z0-9_.-]/g, '').slice(0, 80)
156
- : 'RetentionMaintenanceLaunchError',
157
- },
158
- outcome: 'failure',
159
- });
160
- throw error;
161
384
  }
385
+ return Object.freeze(receipt);
162
386
  }
163
387
  `,
164
388
  'infra/product.ts': `import {
165
389
  createManagedLogGroupContract,
166
390
  createRouterOriginProofInjection,
167
- provisionDatabaseBindings,
391
+ ${sql ? ' provisionDatabaseBindings,\n' : ''}\
168
392
  } from '@empire-builder-kit/nx/foundation';
169
- import { validateReleaseManifest } from '@empire-builder-kit/nx/release';
170
393
  import * as random from '@pulumi/random';
171
394
  import { createHash } from 'node:crypto';
172
- import {
173
- lstatSync,
174
- readFileSync,
175
- readdirSync,
176
- realpathSync,
177
- } from 'node:fs';
178
- import { fileURLToPath } from 'node:url';
395
+ import { readFileSync } from 'node:fs';
179
396
  import {
180
397
  ${identifierPrefix}CleanupRecordExport,
181
398
  ${identifierPrefix}DatabaseHealth,
@@ -203,20 +420,33 @@ import {
203
420
  ${identifierPrefix}UseRecordDelegation,
204
421
  } from '../api/contracts/protected.js';
205
422
  import lifecycleConsumers from '../data/lifecycle-consumers.json';
206
- import databaseBindings from '../database/bindings.json';
423
+ import { EventContractSchema } from '../contracts/events.js';
424
+ import { sameSliceEventDetailTypes } from './event-subscriptions.js';
425
+ ${sql ? "import databaseBindings from '../database/bindings.json';\n" : ''}\
426
+ ${sql ? "import { createSqlOutboxInfrastructure } from './sql-outbox.js';\n" : ''}\
427
+ ${"import { createKeyValueInfrastructure } from './key-value.js';\n"}\
428
+ ${"import { createKeyValueRetentionInfrastructure } from './key-value-retention.js';\n"}\
207
429
  import localConfiguration from '../configuration/local-values.json';
430
+ import backgroundWorkPolicy from '../operations/background-work.json';
431
+ import { backgroundWorkSchedules } from './background-work.js';
208
432
  import { ConfigurationDeclarations } from '../configuration/declarations.js';
209
433
  import { assertAsyncAdapterDeploymentReady } from '../async/recovery-adapters.js';
210
434
  import {
211
435
  getFoundationInfrastructure,
212
- registerProductMigration,
436
+ ${sql ? ' registerProductMigration,\n' : ''}\
213
437
  registerProductRetention,
214
438
  } from './foundation.js';
439
+ import { createVpcRouteFunctionMaterializer } from './route-function-materialization.js';
440
+ import { removeFinderMetadataFromBuiltFunction } from './next-build-authority.js';
215
441
  import {
442
+ createStandardSliceNativeRuntimeAuthority,
443
+ } from './native-runtime-authority/index.js';
444
+ ${sql
445
+ ? `import {
216
446
  standardSliceDatabaseTaskFamily,
217
- standardSliceResourceNames,
218
- } from './workload-roles/effective-intent.js';
219
- import type { StandardSliceWorkloadRolePreflight } from './workload-roles/preflight.js';
447
+ } from './native-runtime-authority/effective-intent.js';
448
+ `
449
+ : ''}\
220
450
  import { createStandardSliceSecretLinks } from './personal-secrets.js';
221
451
  import { create${pascalName}PerformanceCostControls } from './performance-cost-controls.js';
222
452
  import {
@@ -226,10 +456,11 @@ import {
226
456
  } from './authorization.js';
227
457
  import { createStandardDataExportStore } from './data-export.js';
228
458
  import { create${pascalName}Reliability } from './reliability.js';
229
- import { resolveStandardContainerImage } from './container-images.js';
459
+ ${sql ? "import { resolveStandardContainerImage } from './container-images.js';\n" : ''}\
230
460
  import {
231
461
  defineEventConsumerAlarms,
232
462
  } from '@empire-builder-kit/runtime/observability';
463
+ import { validateSuccessfulDeploymentReceipt } from './deployment-receipt.js';
233
464
  // <ebk:composition-imports:start>
234
465
  // <ebk:composition-imports:end>
235
466
 
@@ -244,11 +475,12 @@ function protectedPerformanceDeploymentAuthority(input: {
244
475
  );
245
476
  }
246
477
  if (input.enabled === 'false') return undefined;
247
- if (input.stage !== 'staging' && input.stage !== 'production') {
478
+ if (!${JSON.stringify(protectedRuntimeStages)}.includes(input.stage)) {
248
479
  throw new Error(
249
- 'Protected performance load can be deployed only to staging or production.',
480
+ 'Protected performance load requires one active rehearsal or production stage.',
250
481
  );
251
482
  }
483
+ const stage = input.stage as 'development' | 'production' | 'staging';
252
484
  const accountId = process.env.AWS_EXPECTED_ACCOUNT_ID?.trim() ?? '';
253
485
  const region = process.env.AWS_REGION?.trim() ?? '';
254
486
  if (
@@ -284,60 +516,38 @@ function protectedPerformanceDeploymentAuthority(input: {
284
516
  'Protected performance load requires the exact deployed release candidate.',
285
517
  );
286
518
  }
287
- const releaseManifestDirectory = new URL(
288
- '../.ebk/evidence/release-manifests/',
289
- import.meta.url,
290
- );
291
- const releaseManifestNames = readdirSync(releaseManifestDirectory).filter(
292
- (entry) => /^release-[a-f0-9]{24}[.]json$/.test(entry),
293
- );
294
- if (releaseManifestNames.length !== 1) {
295
- throw new Error(
296
- 'Protected performance load requires one exact deployed release manifest.',
297
- );
298
- }
299
- const releaseManifestUrl = new URL(
300
- releaseManifestNames[0]!,
301
- releaseManifestDirectory,
519
+ const deploymentReceiptBytes = readFileSync(
520
+ new URL(
521
+ '../../../.ebk/evidence/deployment/latest.json',
522
+ import.meta.url,
523
+ ),
302
524
  );
303
- const releaseManifestPath = fileURLToPath(releaseManifestUrl);
304
- const releaseManifestMetadata = lstatSync(releaseManifestPath);
305
- if (
306
- !releaseManifestMetadata.isFile() ||
307
- releaseManifestMetadata.isSymbolicLink() ||
308
- releaseManifestMetadata.size <= 0 ||
309
- releaseManifestMetadata.size > 10 * 1024 * 1024 ||
310
- realpathSync(releaseManifestPath) !== releaseManifestPath
311
- ) {
312
- throw new Error(
313
- 'Protected performance load release manifest is not one bounded canonical file.',
314
- );
315
- }
316
- const releaseManifest = validateReleaseManifest(
317
- JSON.parse(readFileSync(releaseManifestPath, 'utf8')),
525
+ validateSuccessfulDeploymentReceipt(
526
+ JSON.parse(deploymentReceiptBytes.toString('utf8')),
318
527
  {
319
- requireSupplyChain: true,
528
+ account: accountId,
529
+ project: '${name}',
530
+ region,
320
531
  sourceCommit: input.releaseSourceCommit,
532
+ stage,
321
533
  },
322
534
  );
323
- if (releaseManifestNames[0] !== releaseManifest.releaseId + '.json') {
324
- throw new Error(
325
- 'Protected performance load release manifest path differs from its identity.',
326
- );
327
- }
535
+ const deploymentReceiptDigest = createHash('sha256')
536
+ .update(deploymentReceiptBytes)
537
+ .digest('hex');
328
538
  return Object.freeze({
329
539
  candidateDigest: createHash('sha256')
330
540
  .update(candidateBytes)
331
541
  .digest('hex'),
332
- releaseId: releaseManifest.releaseId,
333
- releaseMaterialDigest: releaseManifest.materialDigest,
542
+ releaseId: 'release-' + deploymentReceiptDigest.slice(0, 24),
543
+ releaseMaterialDigest: deploymentReceiptDigest,
334
544
  target: Object.freeze({
335
545
  accountFingerprint:
336
546
  'sha256:' +
337
547
  createHash('sha256').update(accountId, 'utf8').digest('hex'),
338
548
  region,
339
549
  slice: '${name}',
340
- stage: input.stage as 'production' | 'staging',
550
+ stage,
341
551
  }),
342
552
  });
343
553
  }
@@ -448,19 +658,15 @@ function lambdaLogging(logGroupName: any, functionName?: any) {
448
658
  };
449
659
  }
450
660
 
451
- export function product(workloadRoles?: StandardSliceWorkloadRolePreflight) {
452
- if (!$dev && !workloadRoles) {
453
- throw new Error(
454
- 'Protected workload-role preflight must complete before infrastructure construction.',
455
- );
456
- }
457
- const workloadRoleBindings = workloadRoles?.bindings;
661
+ export function product() {
662
+ const backgroundSchedules = backgroundWorkSchedules(
663
+ backgroundWorkPolicy,
664
+ $dev ? 'development' : $app.stage,
665
+ );
458
666
  const asyncAdaptersConfigured = assertAsyncAdapterDeploymentReady();
459
- const secretLinks = createStandardSliceSecretLinks({
460
- protectedAccess: workloadRoles?.secretAccess,
461
- });
667
+ const secretLinks = createStandardSliceSecretLinks();
462
668
  const performanceHmacLogicalName =
463
- '${pascalName}PerformanceLoadHmacKeys';
669
+ 'GeneratedPerformanceLoadHmacKeys';
464
670
  const secretLinksFor = (
465
671
  resource: string,
466
672
  excludeLogicalNames: readonly string[] = [],
@@ -473,21 +679,6 @@ export function product(workloadRoles?: StandardSliceWorkloadRolePreflight) {
473
679
  ];
474
680
  // Workload generators compose multi-role secret bindings through this seam.
475
681
  void secretLinksForWorkloads;
476
- const workloadRoleResourceNames = workloadRoles
477
- ? standardSliceResourceNames(workloadRoles.intentContext)
478
- : undefined;
479
- if (workloadRoleResourceNames) {
480
- $transform(aws.dynamodb.Table, (args: any, _opts: any, logicalName: string) => {
481
- if (logicalName === '${pascalName}WebRevalidationTable') {
482
- args.name = workloadRoleResourceNames.webRevalidationTable;
483
- }
484
- });
485
- $transform(aws.sqs.Queue, (args: any, _opts: any, logicalName: string) => {
486
- if (logicalName === '${pascalName}WebRevalidationEventsQueue') {
487
- args.name = workloadRoleResourceNames.webRevalidationQueue;
488
- }
489
- });
490
- }
491
682
  const devPortInput = $dev ? (process.env.EBK_DEV_PORT ?? '3000') : '3000';
492
683
  if (!/^\\d+$/.test(devPortInput)) {
493
684
  throw new Error('EBK_DEV_PORT must be an integer from 1024 through 65535.');
@@ -496,17 +687,21 @@ export function product(workloadRoles?: StandardSliceWorkloadRolePreflight) {
496
687
  if (!Number.isSafeInteger(devPort) || devPort < 1024 || devPort > 65535) {
497
688
  throw new Error('EBK_DEV_PORT must be an integer from 1024 through 65535.');
498
689
  }
499
- const protectedStage =
500
- $app.stage === 'staging' || $app.stage === 'production';
690
+ const protectedStage = ${JSON.stringify(protectedRuntimeStages)}.includes(
691
+ $app.stage,
692
+ );
501
693
  const sourceCommit =
502
694
  process.env.EBK_SOURCE_COMMIT ?? (protectedStage ? '' : 'unresolved');
503
695
  const releaseSourceCommit =
504
696
  process.env.EBK_RELEASE_SOURCE_COMMIT ?? sourceCommit;
505
- const exactDevelopmentRetirement =
697
+ ${sql
698
+ ? ` const exactDevelopmentRetirement =
506
699
  $app.stage === 'development' &&
507
700
  Object.values(databaseBindings).some((binding) =>
508
701
  binding.provisioned.includes('retirement_rehearsal'),
509
702
  );
703
+ `
704
+ : ''}\
510
705
  if (
511
706
  protectedStage &&
512
707
  (!/^(?:[a-f0-9]{40}|[a-f0-9]{64})$/.test(sourceCommit) ||
@@ -516,16 +711,31 @@ export function product(workloadRoles?: StandardSliceWorkloadRolePreflight) {
516
711
  'Protected infrastructure requires exact deployment and release source Git object IDs.',
517
712
  );
518
713
  }
519
- const dataRetentionContainer = resolveStandardContainerImage({
520
- artifactId: '${name}.job.data-retention',
521
- local: {
522
- context: '../..',
523
- dockerfile: 'packages/${name}/jobs/data-retention/Dockerfile',
714
+ $transform(
715
+ aws.lambda.Invocation,
716
+ (args: any, _opts: any, logicalName: string) => {
717
+ if (logicalName !== '${pascalName}WebRevalidationSeed') return;
718
+ const triggers = args.triggers;
719
+ if (
720
+ !triggers ||
721
+ typeof triggers !== 'object' ||
722
+ Array.isArray(triggers) ||
723
+ Object.keys(triggers).length !== 1 ||
724
+ typeof triggers.version !== 'string'
725
+ ) {
726
+ throw new Error(
727
+ 'Pinned SST Next revalidation seed does not expose one exact version trigger.',
728
+ );
729
+ }
730
+ // SST 4.17.1 uses Date.now() here, which makes every preview propose a
731
+ // replacement even when the immutable application source is unchanged.
732
+ // Re-seed exactly once per released source instead.
733
+ args.triggers = { version: releaseSourceCommit };
524
734
  },
525
- sourceCommit: releaseSourceCommit,
526
- stage: $app.stage,
527
- });
528
- const databaseMigrationContainer = resolveStandardContainerImage({
735
+ );
736
+ ${''}\
737
+ ${sql
738
+ ? ` const databaseMigrationContainer = resolveStandardContainerImage({
529
739
  artifactId: '${name}.job.database-migration',
530
740
  local: {
531
741
  context: '../..',
@@ -544,6 +754,8 @@ export function product(workloadRoles?: StandardSliceWorkloadRolePreflight) {
544
754
  sourceCommit: releaseSourceCommit,
545
755
  stage: $app.stage,
546
756
  });
757
+ `
758
+ : ''}\
547
759
  const performanceDeploymentAuthority = protectedStage
548
760
  ? protectedPerformanceDeploymentAuthority({
549
761
  enabled: process.env.EBK_PERFORMANCE_LOAD_ENABLED ?? 'false',
@@ -552,6 +764,172 @@ export function product(workloadRoles?: StandardSliceWorkloadRolePreflight) {
552
764
  })
553
765
  : undefined;
554
766
  const foundation = getFoundationInfrastructure();
767
+ ${` const keyValue = createKeyValueInfrastructure({
768
+ eventBusArn: foundation.discovery.eventBusArn,
769
+ eventBusName: foundation.discovery.eventBusName,
770
+ alertTopicArn: foundation.discovery.alertTopicArn,
771
+ outbox: backgroundSchedules.outbox,
772
+ managedLogGroup,
773
+ lambdaLogging,
774
+ });
775
+ `}\
776
+ // Domain delivery is the key-value stream plus scheduled reconciliation; the
777
+ // async/outbox-relay Function drains only the isolated performance sink.
778
+ // Observation only: alarms never pause, drop, or retry an event intent.
779
+ const keyValueStreamDeliveryErrorAlarm = new aws.cloudwatch.MetricAlarm(
780
+ '${pascalName}KeyValueStreamDeliveryErrors',
781
+ {
782
+ alarmActions: [foundation.discovery.alertTopicArn],
783
+ alarmDescription: operationalAlarmDescription({
784
+ businessSymptom:
785
+ 'The key-value stream delivery Function failed an invocation.',
786
+ id: 'key-value-stream-delivery-errors',
787
+ runbook: 'docs/runbooks/async-recovery.md',
788
+ }),
789
+ comparisonOperator: 'GreaterThanOrEqualToThreshold',
790
+ dimensions: { FunctionName: keyValue.dispatcher.name },
791
+ evaluationPeriods: 1,
792
+ metricName: 'Errors',
793
+ name: '${name}-' + $app.stage + '-key-value-stream-delivery-errors',
794
+ namespace: 'AWS/Lambda',
795
+ period: 300,
796
+ statistic: 'Sum',
797
+ threshold: 1,
798
+ treatMissingData: 'notBreaching',
799
+ },
800
+ );
801
+ const keyValueStreamIteratorAgeAlarm = new aws.cloudwatch.MetricAlarm(
802
+ '${pascalName}KeyValueStreamIteratorAge',
803
+ {
804
+ alarmActions: [foundation.discovery.alertTopicArn],
805
+ alarmDescription: operationalAlarmDescription({
806
+ businessSymptom:
807
+ 'Prompt domain-event delivery from the key-value stream is falling behind.',
808
+ id: 'key-value-stream-iterator-age',
809
+ runbook: 'docs/runbooks/async-recovery.md',
810
+ }),
811
+ comparisonOperator: 'GreaterThanOrEqualToThreshold',
812
+ dimensions: { FunctionName: keyValue.dispatcher.name },
813
+ evaluationPeriods: 1,
814
+ metricName: 'IteratorAge',
815
+ name: '${name}-' + $app.stage + '-key-value-stream-iterator-age',
816
+ namespace: 'AWS/Lambda',
817
+ period: 300,
818
+ statistic: 'Maximum',
819
+ threshold: 600_000,
820
+ treatMissingData: 'notBreaching',
821
+ },
822
+ );
823
+ const keyValueReconciliationErrorAlarm = new aws.cloudwatch.MetricAlarm(
824
+ '${pascalName}KeyValueReconciliationErrors',
825
+ {
826
+ alarmActions: [foundation.discovery.alertTopicArn],
827
+ alarmDescription: operationalAlarmDescription({
828
+ businessSymptom: 'Durable domain-event recovery is failing.',
829
+ id: 'key-value-reconciliation-errors',
830
+ runbook: 'docs/runbooks/async-recovery.md',
831
+ }),
832
+ comparisonOperator: 'GreaterThanOrEqualToThreshold',
833
+ dimensions: { FunctionName: keyValue.reconciler.name },
834
+ evaluationPeriods: 1,
835
+ metricName: 'Errors',
836
+ name: '${name}-' + $app.stage + '-key-value-reconciliation-errors',
837
+ namespace: 'AWS/Lambda',
838
+ period: 300,
839
+ statistic: 'Sum',
840
+ threshold: 1,
841
+ treatMissingData: 'notBreaching',
842
+ },
843
+ );
844
+ // Reconciliation gauges are bounded per-run samples (one page per shard),
845
+ // so hourly periods cover the default and any sub-hourly reconciliation
846
+ // cadence. A slower configured cadence leaves empty periods, so these alarms
847
+ // can miss a backlog (see each registration's expectedFalseNegativeRisk).
848
+ const outboxBacklogAlarm = new aws.cloudwatch.MetricAlarm(
849
+ '${pascalName}OutboxBacklog',
850
+ {
851
+ alarmActions: [foundation.discovery.alertTopicArn],
852
+ alarmDescription: operationalAlarmDescription({
853
+ businessSymptom: 'Durable domain-event intents remain undelivered.',
854
+ id: 'outbox-backlog',
855
+ runbook: 'docs/runbooks/async-recovery.md',
856
+ }),
857
+ comparisonOperator: 'GreaterThanOrEqualToThreshold',
858
+ dimensions: { Slice: '${name}', Stage: $app.stage },
859
+ evaluationPeriods: 2,
860
+ metricName: 'OutboxBacklog',
861
+ name: '${name}-' + $app.stage + '-outbox-backlog',
862
+ namespace: 'EBK/Async',
863
+ period: 3600,
864
+ statistic: 'Maximum',
865
+ threshold: 1,
866
+ treatMissingData: 'notBreaching',
867
+ },
868
+ );
869
+ const eventIntentRejectedAlarm = new aws.cloudwatch.MetricAlarm(
870
+ '${pascalName}EventIntentRejected',
871
+ {
872
+ alarmActions: [foundation.discovery.alertTopicArn],
873
+ alarmDescription: operationalAlarmDescription({
874
+ businessSymptom: 'A retained domain-event intent cannot be delivered.',
875
+ id: 'event-intent-rejected',
876
+ runbook: 'docs/runbooks/async-recovery.md',
877
+ }),
878
+ comparisonOperator: 'GreaterThanOrEqualToThreshold',
879
+ dimensions: { Slice: '${name}', Stage: $app.stage },
880
+ evaluationPeriods: 1,
881
+ metricName: 'EventReconciliationRejected',
882
+ name: '${name}-' + $app.stage + '-event-intent-rejected',
883
+ namespace: 'EBK/Async',
884
+ period: 3600,
885
+ statistic: 'Maximum',
886
+ threshold: 1,
887
+ treatMissingData: 'notBreaching',
888
+ },
889
+ );
890
+ const eventDeliveryAttemptsAlarm = new aws.cloudwatch.MetricAlarm(
891
+ '${pascalName}EventDeliveryAttempts',
892
+ {
893
+ alarmActions: [foundation.discovery.alertTopicArn],
894
+ alarmDescription: operationalAlarmDescription({
895
+ businessSymptom:
896
+ 'A domain-event intent is repeatedly failing delivery.',
897
+ id: 'event-delivery-attempts',
898
+ runbook: 'docs/runbooks/async-recovery.md',
899
+ }),
900
+ comparisonOperator: 'GreaterThanOrEqualToThreshold',
901
+ dimensions: { Slice: '${name}', Stage: $app.stage },
902
+ evaluationPeriods: 1,
903
+ metricName: 'EventDeliveryMaxAttempts',
904
+ name: '${name}-' + $app.stage + '-event-delivery-attempts',
905
+ namespace: 'EBK/Async',
906
+ period: 3600,
907
+ statistic: 'Maximum',
908
+ threshold: 10,
909
+ treatMissingData: 'notBreaching',
910
+ },
911
+ );
912
+ const nativeAuthority = createStandardSliceNativeRuntimeAuthority({
913
+ ${sql
914
+ ? ` clusterId: foundation.discovery.clusterId,
915
+ clusterResourceId: foundation.database.clusterResourceId,
916
+ masterSecretArn: foundation.database.masterSecretArn,
917
+ `
918
+ : ''}\
919
+ eventBusArn: foundation.discovery.eventBusArn,
920
+ identityUserPoolArn: foundation.discovery.identityUserPoolArn,
921
+ });
922
+ const runtimeResourceNames = nativeAuthority.resourceNames;
923
+ $transform(aws.dynamodb.Table, (args: any, _opts: any, logicalName: string) => {
924
+ if (logicalName === '${pascalName}WebRevalidationTable') {
925
+ args.name = runtimeResourceNames.webRevalidationTable;
926
+ }
927
+ });
928
+ $transform(aws.sqs.Queue, (args: any, _opts: any, logicalName: string) => {
929
+ if (logicalName === '${pascalName}WebRevalidationEventsQueue') {
930
+ args.name = runtimeResourceNames.webRevalidationQueue;
931
+ }
932
+ });
555
933
  const performanceCostControls = create${pascalName}PerformanceCostControls({
556
934
  accountCostPolicyDigest: foundation.discovery.accountCostPolicyDigest,
557
935
  accountMonthlyMaximumUsd:
@@ -559,12 +937,15 @@ export function product(workloadRoles?: StandardSliceWorkloadRolePreflight) {
559
937
  alertTopicArn: foundation.discovery.alertTopicArn,
560
938
  workspace: '${workspace}',
561
939
  });
562
- const databaseProvisions = provisionDatabaseBindings(foundation, {
940
+ ${sql
941
+ ? ` const databaseProvisions = provisionDatabaseBindings(foundation, {
563
942
  bindings: databaseBindings,
564
943
  slice: '${name}',
565
944
  });
566
945
  const databaseProvision = databaseProvisions.bindings.primary;
567
946
  const { db: database } = databaseProvision;
947
+ `
948
+ : ''}\
568
949
  const authClient = foundation.userPool.addClient('${pascalName}WebAuthClient', {
569
950
  transform: {
570
951
  client: {
@@ -584,16 +965,10 @@ export function product(workloadRoles?: StandardSliceWorkloadRolePreflight) {
584
965
  const machineClientProvisioner = new sst.aws.Function(
585
966
  '${pascalName}MachineClientProvisioner',
586
967
  {
587
- ...(workloadRoleBindings?.authorization.provisioner ?? {}),
588
- ...(workloadRoles
589
- ? {}
590
- : {
591
- permissions:
592
- standardSliceMachineClientProvisionerPermissions({
593
- userPoolArn:
594
- foundation.discovery.identityUserPoolArn,
595
- }),
596
- }),
968
+ dev: false,
969
+ permissions: standardSliceMachineClientProvisionerPermissions({
970
+ userPoolArn: foundation.discovery.identityUserPoolArn,
971
+ }),
597
972
  environment: {
598
973
  EBK_SLICE: '${name}',
599
974
  EBK_STAGE: $app.stage,
@@ -617,9 +992,8 @@ export function product(workloadRoles?: StandardSliceWorkloadRolePreflight) {
617
992
  userPoolId: foundation.userPool.id,
618
993
  });
619
994
  const dataExportStore = createStandardDataExportStore({
620
- bucketName: workloadRoleResourceNames?.dataExportBucket,
621
- keyAliasName: workloadRoleResourceNames?.dataExportKeyAlias,
622
- workloadRoleArn: workloadRoleBindings?.api.dataRights.role,
995
+ bucketName: runtimeResourceNames.dataExportBucket,
996
+ keyAliasName: runtimeResourceNames.dataExportKeyAlias,
623
997
  });
624
998
 
625
999
  const apiHealthLogs = managedLogGroup(
@@ -733,9 +1107,35 @@ export function product(workloadRoles?: StandardSliceWorkloadRolePreflight) {
733
1107
  foundation.discovery.alertTopicArn,
734
1108
  );
735
1109
 
1110
+ const vpcRouteFunctionMaterializer =
1111
+ createVpcRouteFunctionMaterializer({
1112
+ directHandlers: [
1113
+ 'api/health.handler',
1114
+ 'async/webhook-ingress.handler',
1115
+ ],
1116
+ ${''}\
1117
+ output: $output,
1118
+ });
736
1119
  const api = new sst.aws.ApiGatewayV2('${pascalName}Api', {
737
- ...(performanceDeploymentAuthority ? { vpc: foundation.vpc } : {}),
1120
+ ${network ? ' ...(performanceDeploymentAuthority ? { vpc: foundation.vpc } : {}),\n' : ''}\
1121
+ transform: {
1122
+ route: {
1123
+ handler: vpcRouteFunctionMaterializer.transform,
1124
+ },
1125
+ },
738
1126
  });
1127
+ const routeInVpc = (
1128
+ rawRoute: string,
1129
+ handler: { handler: string } & Record<string, any>,
1130
+ args?: Record<string, any>,
1131
+ ) =>
1132
+ vpcRouteFunctionMaterializer.route(
1133
+ rawRoute,
1134
+ handler,
1135
+ args,
1136
+ (registeredRoute, registeredHandler, registeredArgs) =>
1137
+ api.route(registeredRoute, registeredHandler, registeredArgs),
1138
+ );
739
1139
  const performanceIngress = performanceDeploymentAuthority
740
1140
  ? (() => {
741
1141
  const originProof = new random.RandomPassword(
@@ -773,7 +1173,7 @@ export function product(workloadRoles?: StandardSliceWorkloadRolePreflight) {
773
1173
  }
774
1174
  : undefined;
775
1175
  api.route(${identifierPrefix}Health.method + ' ' + ${identifierPrefix}Health.path, {
776
- ...(workloadRoleBindings?.api.health ?? {}),
1176
+ permissions: nativeAuthority.permissions('api-health'),
777
1177
  environment: {
778
1178
  EBK_SOURCE_COMMIT: releaseSourceCommit,
779
1179
  EBK_STAGE: $app.stage,
@@ -782,13 +1182,14 @@ export function product(workloadRoles?: StandardSliceWorkloadRolePreflight) {
782
1182
  link: secretLinksFor('api-health'),
783
1183
  ...lambdaLogging(apiHealthLogs.logGroup.name),
784
1184
  });
785
- api.route(
1185
+ routeInVpc(
786
1186
  ${identifierPrefix}MachineAuthorizationProof.method +
787
1187
  ' ' +
788
1188
  ${identifierPrefix}MachineAuthorizationProof.path,
789
1189
  {
790
- ...(workloadRoleBindings?.api.machineAuthorizationProof ?? {}),
1190
+ permissions: nativeAuthority.permissions('api-machine-authorization-proof'),
791
1191
  environment: {
1192
+ ${' ...keyValue.environment,\n'}\
792
1193
  AUTH_CLIENT_ID: authClient.id,
793
1194
  AUTH_MACHINE_ACCEPTED_CLIENT_IDS:
794
1195
  machineAuthorization.acceptedClientIdsJson,
@@ -798,11 +1199,10 @@ export function product(workloadRoles?: StandardSliceWorkloadRolePreflight) {
798
1199
  },
799
1200
  handler: 'api/machine-authorization.handler',
800
1201
  link: [
801
- database,
1202
+ ${'keyValue.link'},
802
1203
  ...secretLinksFor('api-machine-authorization-proof'),
803
1204
  ],
804
1205
  ...lambdaLogging(machineAuthorizationProofTargetLogs.logGroup.name),
805
- vpc: foundation.vpc,
806
1206
  },
807
1207
  );
808
1208
  for (const route of [
@@ -819,9 +1219,10 @@ export function product(workloadRoles?: StandardSliceWorkloadRolePreflight) {
819
1219
  operation: ${identifierPrefix}RevokeRecordDelegation,
820
1220
  },
821
1221
  ]) {
822
- api.route(route.operation.method + ' ' + route.operation.path, {
823
- ...(workloadRoleBindings?.api.delegations ?? {}),
1222
+ routeInVpc(route.operation.method + ' ' + route.operation.path, {
1223
+ permissions: nativeAuthority.permissions('api-delegations'),
824
1224
  environment: {
1225
+ ${' ...keyValue.environment,\n'}\
825
1226
  AUTH_CLIENT_ID: authClient.id,
826
1227
  AUTH_MACHINE_ACCEPTED_CLIENT_IDS:
827
1228
  machineAuthorization.acceptedClientIdsJson,
@@ -830,9 +1231,8 @@ export function product(workloadRoles?: StandardSliceWorkloadRolePreflight) {
830
1231
  EBK_STAGE: $app.stage,
831
1232
  },
832
1233
  handler: route.handler,
833
- link: [database, ...secretLinksFor('api-delegations')],
1234
+ link: [${'keyValue.link'}, ...secretLinksFor('api-delegations')],
834
1235
  ...lambdaLogging(delegationLogs.logGroup.name),
835
- vpc: foundation.vpc,
836
1236
  });
837
1237
  }
838
1238
  for (const route of [
@@ -873,12 +1273,10 @@ export function product(workloadRoles?: StandardSliceWorkloadRolePreflight) {
873
1273
  operation: ${identifierPrefix}ExpireRecordHold,
874
1274
  },
875
1275
  ]) {
876
- api.route(route.operation.method + ' ' + route.operation.path, {
877
- ...(workloadRoleBindings?.api.dataRights ?? {}),
878
- ...(workloadRoles
879
- ? {}
880
- : { permissions: dataExportStore.dataRightsPermissions }),
1276
+ routeInVpc(route.operation.method + ' ' + route.operation.path, {
1277
+ permissions: nativeAuthority.permissions('api-data-rights'),
881
1278
  environment: {
1279
+ ${' ...keyValue.environment,\n'}\
882
1280
  AUTH_CLIENT_ID: authClient.id,
883
1281
  AUTH_MACHINE_ACCEPTED_CLIENT_IDS:
884
1282
  machineAuthorization.acceptedClientIdsJson,
@@ -891,12 +1289,11 @@ export function product(workloadRoles?: StandardSliceWorkloadRolePreflight) {
891
1289
  },
892
1290
  handler: route.handler,
893
1291
  link: [
894
- database,
1292
+ ${'keyValue.link'},
895
1293
  foundation.eventBus,
896
1294
  ...secretLinksFor('api-data-rights'),
897
1295
  ],
898
1296
  ...lambdaLogging(dataRightsLogs.logGroup.name),
899
- vpc: foundation.vpc,
900
1297
  });
901
1298
  }
902
1299
  for (const route of [
@@ -909,9 +1306,10 @@ export function product(workloadRoles?: StandardSliceWorkloadRolePreflight) {
909
1306
  operation: ${identifierPrefix}DecideRetentionApproval,
910
1307
  },
911
1308
  ]) {
912
- api.route(route.operation.method + ' ' + route.operation.path, {
913
- ...(workloadRoleBindings?.api.databaseRecords ?? {}),
1309
+ routeInVpc(route.operation.method + ' ' + route.operation.path, {
1310
+ permissions: nativeAuthority.permissions('api-database-records'),
914
1311
  environment: {
1312
+ ${' ...keyValue.environment,\n'}\
915
1313
  AUTH_CLIENT_ID: authClient.id,
916
1314
  AUTH_MACHINE_ACCEPTED_CLIENT_IDS:
917
1315
  machineAuthorization.acceptedClientIdsJson,
@@ -920,18 +1318,18 @@ export function product(workloadRoles?: StandardSliceWorkloadRolePreflight) {
920
1318
  EBK_STAGE: $app.stage,
921
1319
  },
922
1320
  handler: route.handler,
923
- link: [database, ...secretLinksFor('api-database-records')],
1321
+ link: [${'keyValue.link'}, ...secretLinksFor('api-database-records')],
924
1322
  ...lambdaLogging(dataRetentionReviewLogs.logGroup.name),
925
- vpc: foundation.vpc,
926
1323
  });
927
1324
  }
928
- api.route(
1325
+ routeInVpc(
929
1326
  ${identifierPrefix}ProtectedExample.method +
930
1327
  ' ' +
931
1328
  ${identifierPrefix}ProtectedExample.path,
932
1329
  {
933
- ...(workloadRoleBindings?.api.protected ?? {}),
1330
+ permissions: nativeAuthority.permissions('api-protected'),
934
1331
  environment: {
1332
+ ${' ...keyValue.environment,\n'}\
935
1333
  AUTH_CLIENT_ID: authClient.id,
936
1334
  AUTH_MACHINE_ACCEPTED_CLIENT_IDS:
937
1335
  machineAuthorization.acceptedClientIdsJson,
@@ -940,18 +1338,18 @@ export function product(workloadRoles?: StandardSliceWorkloadRolePreflight) {
940
1338
  EBK_STAGE: $app.stage,
941
1339
  },
942
1340
  handler: 'api/protected.handler',
943
- link: [database, ...secretLinksFor('api-protected')],
1341
+ link: [${'keyValue.link'}, ...secretLinksFor('api-protected')],
944
1342
  ...lambdaLogging(protectedApiLogs.logGroup.name),
945
- vpc: foundation.vpc,
946
1343
  },
947
1344
  );
948
- api.route(
1345
+ routeInVpc(
949
1346
  ${identifierPrefix}DatabaseHealth.method +
950
1347
  ' ' +
951
1348
  ${identifierPrefix}DatabaseHealth.path,
952
1349
  {
953
- ...(workloadRoleBindings?.api.databaseHealth ?? {}),
1350
+ permissions: nativeAuthority.permissions('api-database-health'),
954
1351
  environment: {
1352
+ ${' ...keyValue.environment,\n'}\
955
1353
  AUTH_CLIENT_ID: authClient.id,
956
1354
  AUTH_MACHINE_ACCEPTED_CLIENT_IDS:
957
1355
  machineAuthorization.acceptedClientIdsJson,
@@ -960,9 +1358,8 @@ export function product(workloadRoles?: StandardSliceWorkloadRolePreflight) {
960
1358
  EBK_STAGE: $app.stage,
961
1359
  },
962
1360
  handler: 'database/runtime-health.handler',
963
- link: [database, ...secretLinksFor('api-database-health')],
1361
+ link: [${'keyValue.link'}, ...secretLinksFor('api-database-health')],
964
1362
  ...lambdaLogging(databaseHealthLogs.logGroup.name),
965
- vpc: foundation.vpc,
966
1363
  },
967
1364
  );
968
1365
  for (const route of [
@@ -979,9 +1376,10 @@ export function product(workloadRoles?: StandardSliceWorkloadRolePreflight) {
979
1376
  operation: ${identifierPrefix}PutRecord,
980
1377
  },
981
1378
  ]) {
982
- api.route(route.operation.method + ' ' + route.operation.path, {
983
- ...(workloadRoleBindings?.api.databaseRecords ?? {}),
1379
+ routeInVpc(route.operation.method + ' ' + route.operation.path, {
1380
+ permissions: nativeAuthority.permissions('api-database-records'),
984
1381
  environment: {
1382
+ ${' ...keyValue.environment,\n'}\
985
1383
  AUTH_CLIENT_ID: authClient.id,
986
1384
  AUTH_MACHINE_ACCEPTED_CLIENT_IDS:
987
1385
  machineAuthorization.acceptedClientIdsJson,
@@ -992,12 +1390,11 @@ export function product(workloadRoles?: StandardSliceWorkloadRolePreflight) {
992
1390
  },
993
1391
  handler: route.handler,
994
1392
  link: [
995
- database,
1393
+ ${'keyValue.link'},
996
1394
  foundation.eventBus,
997
1395
  ...secretLinksFor('api-database-records'),
998
1396
  ],
999
1397
  ...lambdaLogging(databaseRecordsLogs.logGroup.name),
1000
- vpc: foundation.vpc,
1001
1398
  });
1002
1399
  }
1003
1400
  for (const route of [
@@ -1010,9 +1407,10 @@ export function product(workloadRoles?: StandardSliceWorkloadRolePreflight) {
1010
1407
  operation: ${identifierPrefix}DecideRecordDeletion,
1011
1408
  },
1012
1409
  ]) {
1013
- api.route(route.operation.method + ' ' + route.operation.path, {
1014
- ...(workloadRoleBindings?.api.databaseRecords ?? {}),
1410
+ routeInVpc(route.operation.method + ' ' + route.operation.path, {
1411
+ permissions: nativeAuthority.permissions('api-database-records'),
1015
1412
  environment: {
1413
+ ${' ...keyValue.environment,\n'}\
1016
1414
  AUTH_CLIENT_ID: authClient.id,
1017
1415
  AUTH_MACHINE_ACCEPTED_CLIENT_IDS:
1018
1416
  machineAuthorization.acceptedClientIdsJson,
@@ -1021,18 +1419,18 @@ export function product(workloadRoles?: StandardSliceWorkloadRolePreflight) {
1021
1419
  EBK_STAGE: $app.stage,
1022
1420
  },
1023
1421
  handler: route.handler,
1024
- link: [database, ...secretLinksFor('api-database-records')],
1422
+ link: [${'keyValue.link'}, ...secretLinksFor('api-database-records')],
1025
1423
  ...lambdaLogging(databaseRecordsLogs.logGroup.name),
1026
- vpc: foundation.vpc,
1027
1424
  });
1028
1425
  }
1029
- api.route(
1426
+ routeInVpc(
1030
1427
  ${identifierPrefix}PublishCreated.method +
1031
1428
  ' ' +
1032
1429
  ${identifierPrefix}PublishCreated.path,
1033
1430
  {
1034
- ...(workloadRoleBindings?.api.publishCreated ?? {}),
1431
+ permissions: nativeAuthority.permissions('api-publish-created'),
1035
1432
  environment: {
1433
+ ${' ...keyValue.environment,\n'}\
1036
1434
  AUTH_CLIENT_ID: authClient.id,
1037
1435
  AUTH_MACHINE_ACCEPTED_CLIENT_IDS:
1038
1436
  machineAuthorization.acceptedClientIdsJson,
@@ -1043,21 +1441,17 @@ export function product(workloadRoles?: StandardSliceWorkloadRolePreflight) {
1043
1441
  },
1044
1442
  handler: 'api/publish-created.handler',
1045
1443
  link: [
1046
- database,
1444
+ ${'keyValue.link'},
1047
1445
  foundation.eventBus,
1048
1446
  ...secretLinksFor('api-publish-created'),
1049
1447
  ],
1050
1448
  ...lambdaLogging(eventPublisherLogs.logGroup.name),
1051
- vpc: foundation.vpc,
1052
1449
  },
1053
1450
  );
1054
1451
  const machineAuthorizationProof = new sst.aws.Function(
1055
1452
  '${pascalName}MachineAuthorizationProof',
1056
1453
  {
1057
- ...(workloadRoleBindings?.authorization.proof ?? {}),
1058
- ...(workloadRoles
1059
- ? {}
1060
- : { permissions: machineAuthorization.proof.permissions }),
1454
+ permissions: machineAuthorization.proof.permissions,
1061
1455
  environment: {
1062
1456
  ...machineAuthorization.proof.environment,
1063
1457
  AUTH_MACHINE_PROBE_URL:
@@ -1072,11 +1466,9 @@ export function product(workloadRoles?: StandardSliceWorkloadRolePreflight) {
1072
1466
  const dataExportCleanup = new sst.aws.Function(
1073
1467
  '${pascalName}DataExportCleanup',
1074
1468
  {
1075
- ...(workloadRoleBindings?.dataExport.cleanup ?? {}),
1076
- ...(workloadRoles
1077
- ? {}
1078
- : { permissions: dataExportStore.cleanupPermissions }),
1469
+ permissions: dataExportStore.cleanupPermissions,
1079
1470
  environment: {
1471
+ ${' ...keyValue.environment,\n'}\
1080
1472
  DATA_EXPORT_BUCKET_NAME: dataExportStore.bucket.bucket,
1081
1473
  DATA_EXPORT_KMS_KEY_ARN: dataExportStore.encryptionKey.arn,
1082
1474
  EBK_STAGE: $app.stage,
@@ -1084,24 +1476,21 @@ export function product(workloadRoles?: StandardSliceWorkloadRolePreflight) {
1084
1476
  },
1085
1477
  handler: 'jobs/data-export-cleanup.handler',
1086
1478
  link: [
1087
- database,
1479
+ ${'keyValue.link'},
1088
1480
  ...secretLinksFor('data-export-cleanup'),
1089
1481
  ],
1090
1482
  ...lambdaLogging(dataExportCleanupLogs.logGroup.name),
1091
1483
  timeout: '5 minutes',
1092
- vpc: foundation.vpc,
1484
+ ${''}\
1093
1485
  },
1094
1486
  );
1095
1487
  const dataExportCleanupRule = new aws.cloudwatch.EventRule(
1096
1488
  '${pascalName}DataExportCleanupSchedule',
1097
1489
  {
1098
- ...(workloadRoleResourceNames
1099
- ? { name: workloadRoleResourceNames.dataExportCleanupRule }
1100
- : {}),
1490
+ name: runtimeResourceNames.dataExportCleanupRule,
1101
1491
  description:
1102
1492
  'Hourly authoritative cleanup of expired confidential export versions.',
1103
- scheduleExpression: 'rate(1 hour)',
1104
- state: 'ENABLED',
1493
+ ...backgroundSchedules.exportCleanup,
1105
1494
  tags: {
1106
1495
  'ebk:management': 'standard-data-export',
1107
1496
  'ebk:slice': '${name}',
@@ -1146,9 +1535,7 @@ export function product(workloadRoles?: StandardSliceWorkloadRolePreflight) {
1146
1535
  const eventDlq = new sst.aws.Queue('${pascalName}EventDlq', {
1147
1536
  transform: {
1148
1537
  queue: {
1149
- ...(workloadRoleResourceNames
1150
- ? { name: workloadRoleResourceNames.eventDlq }
1151
- : {}),
1538
+ name: runtimeResourceNames.eventDlq,
1152
1539
  messageRetentionSeconds: 14 * 24 * 60 * 60,
1153
1540
  },
1154
1541
  },
@@ -1157,9 +1544,7 @@ export function product(workloadRoles?: StandardSliceWorkloadRolePreflight) {
1157
1544
  dlq: { queue: eventDlq.arn, retry: 5 },
1158
1545
  transform: {
1159
1546
  queue: {
1160
- ...(workloadRoleResourceNames
1161
- ? { name: workloadRoleResourceNames.eventQueue }
1162
- : {}),
1547
+ name: runtimeResourceNames.eventQueue,
1163
1548
  messageRetentionSeconds: 4 * 24 * 60 * 60,
1164
1549
  visibilityTimeoutSeconds: 120,
1165
1550
  },
@@ -1171,9 +1556,7 @@ export function product(workloadRoles?: StandardSliceWorkloadRolePreflight) {
1171
1556
  attributes: [{ name: 'report_digest', type: 'S' }],
1172
1557
  billingMode: 'PAY_PER_REQUEST',
1173
1558
  hashKey: 'report_digest',
1174
- ...(workloadRoleResourceNames
1175
- ? { name: workloadRoleResourceNames.asyncRecoveryLedger }
1176
- : {}),
1559
+ name: runtimeResourceNames.asyncRecoveryLedger,
1177
1560
  pointInTimeRecovery: { enabled: true },
1178
1561
  tags: {
1179
1562
  'ebk:async-recovery-ledger': 'true',
@@ -1188,19 +1571,18 @@ export function product(workloadRoles?: StandardSliceWorkloadRolePreflight) {
1188
1571
  );
1189
1572
  eventQueue.subscribe({
1190
1573
  environment: {
1574
+ ${' ...keyValue.environment,\n'}\
1191
1575
  EBK_STAGE: $app.stage,
1192
1576
  EBK_WORKLOAD_IDENTITY: '${name}:event-consumer',
1193
1577
  },
1194
1578
  handler: 'api/event-consumer.handler',
1195
- link: [database, ...secretLinksFor('event-consumer')],
1579
+ link: [${'keyValue.link'}, ...secretLinksFor('event-consumer')],
1580
+ permissions: nativeAuthority.permissions('event-consumer'),
1196
1581
  ...lambdaLogging(eventConsumerLogs.logGroup.name),
1197
1582
  timeout: '1 minute',
1198
- vpc: foundation.vpc,
1583
+ ${''}\
1199
1584
  }, {
1200
1585
  batch: { partialResponses: true },
1201
- transform: {
1202
- function: workloadRoleBindings?.queue.eventConsumer,
1203
- },
1204
1586
  });
1205
1587
  foundation.eventBus.subscribeQueue(
1206
1588
  '${pascalName}CreatedEventRule',
@@ -1208,7 +1590,7 @@ export function product(workloadRoles?: StandardSliceWorkloadRolePreflight) {
1208
1590
  {
1209
1591
  pattern: {
1210
1592
  detail: { meta: { stage: [$app.stage] } },
1211
- detailType: ['${pascalName}Created'],
1593
+ detailType: sameSliceEventDetailTypes(EventContractSchema),
1212
1594
  source: ['${name}'],
1213
1595
  },
1214
1596
  },
@@ -1258,9 +1640,7 @@ export function product(workloadRoles?: StandardSliceWorkloadRolePreflight) {
1258
1640
  {
1259
1641
  transform: {
1260
1642
  queue: {
1261
- ...(workloadRoleResourceNames
1262
- ? { name: workloadRoleResourceNames.asyncTargetDlq }
1263
- : {}),
1643
+ name: runtimeResourceNames.asyncTargetDlq,
1264
1644
  messageRetentionSeconds: 14 * 24 * 60 * 60,
1265
1645
  tags: {
1266
1646
  'ebk:async-recovery-source': 'async-target-dlq',
@@ -1274,9 +1654,7 @@ export function product(workloadRoles?: StandardSliceWorkloadRolePreflight) {
1274
1654
  const providerDlq = new sst.aws.Queue('${pascalName}ProviderDlq', {
1275
1655
  transform: {
1276
1656
  queue: {
1277
- ...(workloadRoleResourceNames
1278
- ? { name: workloadRoleResourceNames.providerDlq }
1279
- : {}),
1657
+ name: runtimeResourceNames.providerDlq,
1280
1658
  messageRetentionSeconds: 14 * 24 * 60 * 60,
1281
1659
  tags: {
1282
1660
  'ebk:async-recovery-source': 'provider-dlq',
@@ -1290,9 +1668,7 @@ export function product(workloadRoles?: StandardSliceWorkloadRolePreflight) {
1290
1668
  dlq: { queue: providerDlq.arn, retry: 5 },
1291
1669
  transform: {
1292
1670
  queue: {
1293
- ...(workloadRoleResourceNames
1294
- ? { name: workloadRoleResourceNames.providerQueue }
1295
- : {}),
1671
+ name: runtimeResourceNames.providerQueue,
1296
1672
  messageRetentionSeconds: 4 * 24 * 60 * 60,
1297
1673
  tags: {
1298
1674
  'ebk:async-recovery-destination': 'provider-queue',
@@ -1305,10 +1681,11 @@ export function product(workloadRoles?: StandardSliceWorkloadRolePreflight) {
1305
1681
  });
1306
1682
  const reliability = create${pascalName}Reliability({
1307
1683
  alertTopicArn: foundation.discovery.alertTopicArn,
1308
- alertTopicName:
1309
- foundation.discovery.alertTopicArn.split(':').at(-1) ?? '',
1684
+ alertTopicName: foundation.discovery.alertTopicArn.apply(
1685
+ (alertTopicArn: string) => alertTopicArn.split(':').at(-1) ?? '',
1686
+ ),
1310
1687
  apiId: api.id,
1311
- databaseClusterIdentifier: foundation.discovery.databaseClusterId,
1688
+ ${' databaseTableName: keyValue.table.name,\n'}\
1312
1689
  functionName: dataExportCleanup.name,
1313
1690
  providerQueueName: providerQueue.name,
1314
1691
  });
@@ -1356,6 +1733,9 @@ export function product(workloadRoles?: StandardSliceWorkloadRolePreflight) {
1356
1733
  );
1357
1734
  const runtimeConfiguration = protectedStage && !$dev
1358
1735
  ? (() => {
1736
+ const monitorRole = nativeAuthority.createAppConfigMonitorRole(
1737
+ '${pascalName}ConfigurationMonitorRole',
1738
+ );
1359
1739
  const application = new aws.appconfig.Application(
1360
1740
  '${pascalName}ConfigurationApplication',
1361
1741
  {
@@ -1381,8 +1761,7 @@ export function product(workloadRoles?: StandardSliceWorkloadRolePreflight) {
1381
1761
  reliability.providerDeadline,
1382
1762
  ].map((alarm) => ({
1383
1763
  alarmArn: alarm.arn,
1384
- alarmRoleArn:
1385
- workloadRoleBindings!.configuration.monitor.roleArn,
1764
+ alarmRoleArn: monitorRole.arn,
1386
1765
  })),
1387
1766
  name: $app.stage,
1388
1767
  tags: {
@@ -1523,6 +1902,7 @@ export function product(workloadRoles?: StandardSliceWorkloadRolePreflight) {
1523
1902
  providerQueue.subscribe(
1524
1903
  {
1525
1904
  environment: {
1905
+ ${' ...keyValue.environment,\n'}\
1526
1906
  EBK_STAGE: $app.stage,
1527
1907
  EBK_WORKLOAD_IDENTITY: '${name}:async-provider-worker',
1528
1908
  EBK_WORKSPACE: '${workspace}',
@@ -1530,26 +1910,24 @@ export function product(workloadRoles?: StandardSliceWorkloadRolePreflight) {
1530
1910
  },
1531
1911
  handler: 'async/provider-worker.handler',
1532
1912
  link: [
1533
- database,
1913
+ ${'keyValue.link'},
1534
1914
  providerQueue,
1535
1915
  ...secretLinksFor('async-provider-worker'),
1536
1916
  ],
1917
+ permissions: nativeAuthority.permissions('async-provider-worker'),
1537
1918
  ...lambdaLogging(asyncProviderWorkerLogs.logGroup.name),
1538
1919
  timeout: '1 minute',
1539
- vpc: foundation.vpc,
1920
+ ${''}\
1540
1921
  },
1541
1922
  {
1542
1923
  batch: { partialResponses: true },
1543
- transform: {
1544
- function: workloadRoleBindings?.async.providerWorker,
1545
- },
1546
1924
  },
1547
1925
  configurationDeployment
1548
1926
  ? { dependsOn: [configurationDeployment] }
1549
1927
  : {},
1550
1928
  );
1551
1929
  api.route('POST /${name}/webhooks/provider', {
1552
- ...(workloadRoleBindings?.async.webhookIngress ?? {}),
1930
+ permissions: nativeAuthority.permissions('async-webhook-ingress'),
1553
1931
  environment: {
1554
1932
  EBK_ASYNC_LEDGER_TABLE: asyncRecoveryLedger.name,
1555
1933
  EBK_STAGE: $app.stage,
@@ -1568,8 +1946,10 @@ export function product(workloadRoles?: StandardSliceWorkloadRolePreflight) {
1568
1946
  const asyncOutboxRelay = new sst.aws.Function(
1569
1947
  '${pascalName}AsyncOutboxRelay',
1570
1948
  {
1571
- ...(workloadRoleBindings?.async.outboxRelay ?? {}),
1949
+ permissions: nativeAuthority.permissions('async-outbox-relay'),
1572
1950
  environment: {
1951
+ ${' ...keyValue.environment,\n'}\
1952
+ ${''}\
1573
1953
  EBK_STAGE: $app.stage,
1574
1954
  EBK_WORKLOAD_IDENTITY: '${name}:async-outbox-relay',
1575
1955
  EBK_WORKSPACE: '${workspace}',
@@ -1577,38 +1957,117 @@ export function product(workloadRoles?: StandardSliceWorkloadRolePreflight) {
1577
1957
  },
1578
1958
  handler: 'async/outbox-relay.handler',
1579
1959
  link: [
1580
- database,
1960
+ ${'keyValue.link'},
1581
1961
  foundation.eventBus,
1582
1962
  ...secretLinksFor('async-outbox-relay'),
1583
1963
  ],
1584
1964
  ...lambdaLogging(asyncOutboxRelayLogs.logGroup.name),
1585
1965
  timeout: '1 minute',
1586
- vpc: foundation.vpc,
1966
+ ${''}\
1587
1967
  },
1588
1968
  );
1969
+ ${sql
1970
+ ? ` const sqlOutbox = createSqlOutboxInfrastructure({
1971
+ workspace: '${workspace}',
1972
+ database, eventBus: foundation.eventBus, eventBusName: foundation.discovery.eventBusName,
1973
+ secretLinks: secretLinksFor('async-outbox-relay'),
1974
+ vpc: foundation.vpc, schedule: backgroundSchedules.sqlOutboxRecovery,
1975
+ alertTopicArn: foundation.discovery.alertTopicArn,
1976
+ managedLogGroup, lambdaLogging,
1977
+ ${''}\
1978
+ });
1979
+ const sqlOutboxRelayErrorAlarm = new aws.cloudwatch.MetricAlarm(
1980
+ '${pascalName}SqlOutboxRelayErrors',
1981
+ {
1982
+ alarmActions: [foundation.discovery.alertTopicArn],
1983
+ alarmDescription: operationalAlarmDescription({
1984
+ businessSymptom: 'Relational event delivery is failing.',
1985
+ id: 'sql-outbox-relay-errors',
1986
+ runbook: 'docs/runbooks/async-recovery.md',
1987
+ }),
1988
+ comparisonOperator: 'GreaterThanOrEqualToThreshold',
1989
+ dimensions: { FunctionName: sqlOutbox.relay.name },
1990
+ evaluationPeriods: 1,
1991
+ metricName: 'Errors',
1992
+ name: '${name}-' + $app.stage + '-sql-outbox-relay-errors',
1993
+ namespace: 'AWS/Lambda',
1994
+ period: 300,
1995
+ statistic: 'Sum',
1996
+ threshold: 1,
1997
+ treatMissingData: 'notBreaching',
1998
+ },
1999
+ );
2000
+ // The relay counts every unpublished intent; Minimum ignores healthy
2001
+ // in-flight batches and alarms only when no run in an hour drained it.
2002
+ const sqlOutboxBacklogAlarm = new aws.cloudwatch.MetricAlarm(
2003
+ '${pascalName}SqlOutboxBacklog',
2004
+ {
2005
+ alarmActions: [foundation.discovery.alertTopicArn],
2006
+ alarmDescription: operationalAlarmDescription({
2007
+ businessSymptom: 'Relational event intents remain undelivered.',
2008
+ id: 'sql-outbox-backlog',
2009
+ runbook: 'docs/runbooks/async-recovery.md',
2010
+ }),
2011
+ comparisonOperator: 'GreaterThanOrEqualToThreshold',
2012
+ dimensions: { Slice: '${name}', Stage: $app.stage },
2013
+ evaluationPeriods: 2,
2014
+ metricName: 'SqlOutboxBacklog',
2015
+ name: '${name}-' + $app.stage + '-sql-outbox-backlog',
2016
+ namespace: 'EBK/Async',
2017
+ period: 3600,
2018
+ statistic: 'Minimum',
2019
+ threshold: 1,
2020
+ treatMissingData: 'notBreaching',
2021
+ },
2022
+ );
2023
+ const sqlOutboxAttemptsAlarm = new aws.cloudwatch.MetricAlarm(
2024
+ '${pascalName}SqlOutboxAttempts',
2025
+ {
2026
+ alarmActions: [foundation.discovery.alertTopicArn],
2027
+ alarmDescription: operationalAlarmDescription({
2028
+ businessSymptom:
2029
+ 'A relational event intent is repeatedly failing delivery.',
2030
+ id: 'sql-outbox-attempts',
2031
+ runbook: 'docs/runbooks/async-recovery.md',
2032
+ }),
2033
+ comparisonOperator: 'GreaterThanOrEqualToThreshold',
2034
+ dimensions: { Slice: '${name}', Stage: $app.stage },
2035
+ evaluationPeriods: 1,
2036
+ metricName: 'SqlOutboxMaxAttempts',
2037
+ name: '${name}-' + $app.stage + '-sql-outbox-attempts',
2038
+ namespace: 'EBK/Async',
2039
+ period: 3600,
2040
+ statistic: 'Maximum',
2041
+ threshold: 10,
2042
+ treatMissingData: 'notBreaching',
2043
+ },
2044
+ );
2045
+ `
2046
+ : ''}\
1589
2047
  const asyncInboxCleanup = new sst.aws.Function(
1590
2048
  '${pascalName}AsyncInboxCleanup',
1591
2049
  {
1592
- ...(workloadRoleBindings?.async.inboxCleanup ?? {}),
2050
+ permissions: nativeAuthority.permissions('async-inbox-cleanup'),
1593
2051
  environment: {
2052
+ ${' ...keyValue.environment,\n'}\
1594
2053
  EBK_STAGE: $app.stage,
1595
2054
  EBK_WORKLOAD_IDENTITY: '${name}:async-inbox-cleanup',
1596
2055
  EBK_WORKSPACE: '${workspace}',
1597
2056
  },
1598
2057
  handler: 'async/inbox-cleanup.handler',
1599
2058
  link: [
1600
- database,
2059
+ ${'keyValue.link'},
1601
2060
  ...secretLinksFor('async-inbox-cleanup'),
1602
2061
  ],
1603
2062
  ...lambdaLogging(asyncInboxCleanupLogs.logGroup.name),
1604
2063
  timeout: '5 minutes',
1605
- vpc: foundation.vpc,
2064
+ ${''}\
1606
2065
  },
1607
2066
  );
1608
2067
  const asyncScheduleTarget = new sst.aws.Function(
1609
2068
  '${pascalName}AsyncScheduleTarget',
1610
2069
  {
1611
- ...(workloadRoleBindings?.async.scheduleTarget ?? {}),
2070
+ permissions: nativeAuthority.permissions('async-schedule-target'),
1612
2071
  environment: {
1613
2072
  EBK_STAGE: $app.stage,
1614
2073
  EBK_WORKLOAD_IDENTITY: '${name}:async-schedule-target',
@@ -1623,7 +2082,7 @@ export function product(workloadRoles?: StandardSliceWorkloadRolePreflight) {
1623
2082
  const asyncWorkflowTask = new sst.aws.Function(
1624
2083
  '${pascalName}AsyncWorkflowTask',
1625
2084
  {
1626
- ...(workloadRoleBindings?.async.workflowTask ?? {}),
2085
+ permissions: nativeAuthority.permissions('async-workflow-task'),
1627
2086
  environment: {
1628
2087
  EBK_ASYNC_LEDGER_TABLE: asyncRecoveryLedger.name,
1629
2088
  EBK_STAGE: $app.stage,
@@ -1634,7 +2093,7 @@ export function product(workloadRoles?: StandardSliceWorkloadRolePreflight) {
1634
2093
  link: secretLinksFor('async-workflow-task'),
1635
2094
  ...lambdaLogging(
1636
2095
  asyncWorkflowTaskLogs.logGroup.name,
1637
- workloadRoleResourceNames?.workflowTaskFunction,
2096
+ runtimeResourceNames.workflowTaskFunction,
1638
2097
  ),
1639
2098
  timeout: '15 minutes',
1640
2099
  },
@@ -1655,9 +2114,9 @@ export function product(workloadRoles?: StandardSliceWorkloadRolePreflight) {
1655
2114
  const outboxRelayRule = new aws.cloudwatch.EventRule(
1656
2115
  '${pascalName}OutboxRelaySchedule',
1657
2116
  {
1658
- description: 'Durable outbox relay for committed domain events.',
1659
- scheduleExpression: 'rate(1 minute)',
1660
- state: 'ENABLED',
2117
+ description:
2118
+ 'Durable DynamoDB outbox relay; cadence and pause are independent of SQL capacity.',
2119
+ ...backgroundSchedules.outbox,
1661
2120
  tags: {
1662
2121
  'ebk:slice': '${name}',
1663
2122
  'ebk:stage': $app.stage,
@@ -1669,8 +2128,7 @@ export function product(workloadRoles?: StandardSliceWorkloadRolePreflight) {
1669
2128
  {
1670
2129
  description:
1671
2130
  'Bounded cleanup of expired async inbox idempotency records.',
1672
- scheduleExpression: 'rate(1 hour)',
1673
- state: 'ENABLED',
2131
+ ...backgroundSchedules.inboxCleanup,
1674
2132
  tags: {
1675
2133
  'ebk:slice': '${name}',
1676
2134
  'ebk:stage': $app.stage,
@@ -1778,6 +2236,10 @@ export function product(workloadRoles?: StandardSliceWorkloadRolePreflight) {
1778
2236
  );
1779
2237
  let recordExportWorkflow: any;
1780
2238
  if (!$dev && asyncAdaptersConfigured) {
2239
+ const recordExportWorkflowRole = nativeAuthority.createStepFunctionsRole(
2240
+ '${pascalName}RecordExportWorkflowRole',
2241
+ 'async-workflow-orchestration',
2242
+ );
1781
2243
  recordExportWorkflow = new aws.sfn.StateMachine(
1782
2244
  '${pascalName}RecordExportWorkflow',
1783
2245
  {
@@ -1921,8 +2383,7 @@ export function product(workloadRoles?: StandardSliceWorkloadRolePreflight) {
1921
2383
  'ebk-${recordExportWorkflowName}-' +
1922
2384
  $app.stage +
1923
2385
  '-record-export-v1',
1924
- roleArn:
1925
- workloadRoleBindings!.async.workflowOrchestration.roleArn,
2386
+ roleArn: recordExportWorkflowRole.arn,
1926
2387
  tags: {
1927
2388
  'ebk:slice': '${name}',
1928
2389
  'ebk:stage': $app.stage,
@@ -1933,14 +2394,18 @@ export function product(workloadRoles?: StandardSliceWorkloadRolePreflight) {
1933
2394
  );
1934
2395
  }
1935
2396
 
2397
+ const webServerLogging = lambdaLogging(nextServerLogs.logGroup.name);
1936
2398
  const web = new sst.aws.Nextjs('${pascalName}Web', {
2399
+ buildCommand: 'node ../tools/ebk-next-build-environment.mjs',
2400
+ invalidation: { wait: true },
1937
2401
  dev: {
1938
- command: \`pnpm exec next dev web --hostname 127.0.0.1 --port \${devPort}\`,
2402
+ command: 'node tools/ebk-next-dev.mjs',
1939
2403
  directory: '.',
1940
2404
  title: '${pascalName} web',
1941
2405
  url: \`http://127.0.0.1:\${devPort}/${name}\`,
1942
2406
  },
1943
2407
  environment: {
2408
+ ${' ...keyValue.environment,\n'}\
1944
2409
  AUTH_CLIENT_ID: authClient.id,
1945
2410
  AUTH_USER_POOL_ID: foundation.userPool.id,
1946
2411
  EBK_SOURCE_COMMIT: releaseSourceCommit,
@@ -1967,46 +2432,69 @@ export function product(workloadRoles?: StandardSliceWorkloadRolePreflight) {
1967
2432
  ...runtimeConfigurationEnvironment,
1968
2433
  NEXT_PUBLIC_API_URL: api.url,
1969
2434
  },
1970
- link: [database, foundation.userPool],
2435
+ link: [keyValue.link, ${sql ? 'database, foundation.eventBus, ' : ''}foundation.userPool],
1971
2436
  path: 'web',
1972
2437
  router: { instance: foundation.router, path: '/${name}' },
1973
2438
  transform: {
1974
- assets: workloadRoleResourceNames
1975
- ? {
1976
- transform: {
1977
- bucket: {
1978
- bucket: workloadRoleResourceNames.webAssetsBucket,
1979
- },
1980
- },
1981
- }
1982
- : undefined,
2439
+ assets: {
2440
+ transform: {
2441
+ bucket: {
2442
+ bucket: runtimeResourceNames.webAssetsBucket,
2443
+ },
2444
+ },
2445
+ },
1983
2446
  imageOptimizer: {
1984
- ...(workloadRoleBindings?.next.imageOptimizer ?? {}),
2447
+ permissions: nativeAuthority.permissions('web-image-optimizer'),
2448
+ hook: {
2449
+ async postbuild(directory: string) {
2450
+ removeFinderMetadataFromBuiltFunction(directory);
2451
+ },
2452
+ },
1985
2453
  link: secretLinksFor('web-image-optimizer'),
1986
2454
  ...lambdaLogging(nextImageOptimizerLogs.logGroup.name),
1987
2455
  },
1988
2456
  revalidationEventsSubscriber: {
1989
- ...(workloadRoleBindings?.next.revalidationSubscriber ?? {}),
2457
+ permissions: nativeAuthority.permissions('web-revalidation-subscriber'),
1990
2458
  link: secretLinksFor('web-revalidation-subscriber'),
1991
2459
  ...lambdaLogging(nextRevalidationSubscriberLogs.logGroup.name),
1992
2460
  },
1993
2461
  revalidationSeeder: {
1994
- ...(workloadRoleBindings?.next.revalidationSeeder ?? {}),
2462
+ permissions: nativeAuthority.permissions('web-revalidation-seeder'),
1995
2463
  link: secretLinksFor('web-revalidation-seeder'),
1996
2464
  ...lambdaLogging(nextRevalidationSeederLogs.logGroup.name),
1997
2465
  },
1998
- server: {
1999
- ...(workloadRoleBindings?.next.server ?? {}),
2000
- link: secretLinksFor(
2001
- 'web-server',
2002
- performanceDeploymentBinding
2003
- ? []
2004
- : [performanceHmacLogicalName],
2005
- ),
2006
- ...lambdaLogging(nextServerLogs.logGroup.name),
2466
+ server(args: Record<string, unknown>) {
2467
+ args.permissions = $resolve({
2468
+ inherited: args.permissions,
2469
+ additional: nativeAuthority.permissions('web-server'),
2470
+ }).apply(({ inherited, additional }) => [
2471
+ ...((inherited ?? []) as unknown[]),
2472
+ ...additional,
2473
+ ]);
2474
+ args.hook = {
2475
+ async postbuild(directory: string) {
2476
+ removeFinderMetadataFromBuiltFunction(directory);
2477
+ },
2478
+ };
2479
+ // SST applies this transform to both its dev runtime and deployed
2480
+ // server. Preserve native/site links; secret links are additive.
2481
+ args.link = $output(args.link ?? []).apply((links: unknown[]) => [
2482
+ ...links,
2483
+ ...secretLinksFor(
2484
+ 'web-server',
2485
+ performanceDeploymentBinding
2486
+ ? []
2487
+ : [performanceHmacLogicalName],
2488
+ ),
2489
+ ]);
2490
+ Object.assign(args, webServerLogging);
2491
+ args.transform = {
2492
+ ...webServerLogging.transform,
2493
+ ...($dev ? { role: { tags: { 'ebk:dev-runtime': 'web' } } } : {}),
2494
+ };
2007
2495
  },
2008
2496
  },
2009
- vpc: foundation.vpc,
2497
+ ${sql ? ' vpc: foundation.vpc,\n' : ''}\
2010
2498
  }, configurationDeployment
2011
2499
  ? { dependsOn: [configurationDeployment] }
2012
2500
  : {});
@@ -2027,7 +2515,15 @@ export function product(workloadRoles?: StandardSliceWorkloadRolePreflight) {
2027
2515
  }
2028
2516
 
2029
2517
  const migrationControls: Record<string, any> = {};
2030
- if (!$dev) {
2518
+ ${` Object.assign(migrationControls, createKeyValueRetentionInfrastructure({
2519
+ keyValue, workspace: '${workspace}', eventBusArn: foundation.discovery.eventBusArn,
2520
+ alertTopicArn: foundation.discovery.alertTopicArn, schedule: backgroundSchedules.retention,
2521
+ scheduleName: runtimeResourceNames.dataRetentionMaintenanceRule,
2522
+ managedLogGroup, lambdaLogging, register: registerProductRetention,
2523
+ }));
2524
+ `}\
2525
+ ${sql
2526
+ ? ` if (!$dev) {
2031
2527
  const region = aws.getRegionOutput({}).name;
2032
2528
  const taskArguments = (task: any) => ({
2033
2529
  Cluster: task.cluster,
@@ -2049,33 +2545,38 @@ export function product(workloadRoles?: StandardSliceWorkloadRolePreflight) {
2049
2545
  Environment: [
2050
2546
  {
2051
2547
  Name: 'EBK_DATABASE_LIFECYCLE_CORRELATION_ID',
2052
- Value: '{% $states.input.databaseLifecycleCorrelationId %}',
2548
+ Value:
2549
+ '{% $states.context.Execution.Input.databaseLifecycleCorrelationId %}',
2053
2550
  },
2054
2551
  ...(callback
2055
2552
  ? [
2056
2553
  {
2057
2554
  Name: 'EBK_CANDIDATE_IDENTITY',
2058
- Value: '{% $states.input.candidateIdentity %}',
2555
+ Value:
2556
+ '{% $states.context.Execution.Input.candidateIdentity %}',
2059
2557
  },
2060
2558
  {
2061
2559
  Name: 'EBK_EXECUTION_DATABASE_IDENTIFIER',
2062
- Value: '{% $states.input.databaseIdentifier %}',
2560
+ Value:
2561
+ '{% $states.context.Execution.Input.databaseIdentifier %}',
2063
2562
  },
2064
2563
  {
2065
2564
  Name: 'EBK_EXECUTION_SLICE',
2066
- Value: '{% $states.input.slice %}',
2565
+ Value: '{% $states.context.Execution.Input.slice %}',
2067
2566
  },
2068
2567
  {
2069
2568
  Name: 'EBK_EXECUTION_STAGE',
2070
- Value: '{% $states.input.stage %}',
2569
+ Value: '{% $states.context.Execution.Input.stage %}',
2071
2570
  },
2072
2571
  {
2073
2572
  Name: 'EBK_REGISTRATION_DIGEST',
2074
- Value: '{% $states.input.registrationDigest %}',
2573
+ Value:
2574
+ '{% $states.context.Execution.Input.registrationDigest %}',
2075
2575
  },
2076
2576
  {
2077
2577
  Name: 'EBK_SOURCE_COMMIT',
2078
- Value: '{% $states.input.sourceCommit %}',
2578
+ Value:
2579
+ '{% $states.context.Execution.Input.sourceCommit %}',
2079
2580
  },
2080
2581
  {
2081
2582
  Name: 'EBK_STEP_FUNCTIONS_TASK_TOKEN',
@@ -2091,328 +2592,25 @@ export function product(workloadRoles?: StandardSliceWorkloadRolePreflight) {
2091
2592
  logicalName: string,
2092
2593
  definition: unknown,
2093
2594
  logGroup: any,
2094
- roleArn: string,
2095
- ) =>
2096
- new aws.sfn.StateMachine(logicalName, {
2595
+ roleResource: string,
2596
+ ) => {
2597
+ const role = nativeAuthority.createStepFunctionsRole(
2598
+ logicalName + 'Role',
2599
+ roleResource,
2600
+ );
2601
+ return new aws.sfn.StateMachine(logicalName, {
2097
2602
  definition: $jsonStringify(definition),
2098
2603
  loggingConfiguration: {
2099
2604
  includeExecutionData: false,
2100
2605
  level: 'ERROR',
2101
2606
  logDestination: $interpolate\`\${logGroup.arn}:*\`,
2102
2607
  },
2103
- roleArn,
2608
+ roleArn: role.arn,
2104
2609
  type: 'STANDARD',
2105
2610
  });
2106
-
2107
- if (!databaseProvision.readwriteSecretArn) {
2108
- throw new Error(
2109
- 'The active primary database is missing its read/write credential reference.',
2110
- );
2111
- }
2112
- const retentionTaskLogs = managedLogContract('job-data-retention');
2113
- const retentionTask = new sst.aws.Task('${pascalName}DataRetentionTask', {
2114
- ...workloadRoleBindings!.dataRetention.task,
2115
- cluster: foundation.cluster,
2116
- environment: {
2117
- ...dataRetentionContainer.environment,
2118
- AWS_REGION: region,
2119
- EBK_DATABASE_HOST: foundation.database.clusterEndpoint,
2120
- EBK_DATABASE_NAME: databaseProvision.databaseName,
2121
- EBK_DATABASE_PORT: $interpolate\`\${foundation.database.port}\`,
2122
- EBK_DATABASE_USERNAME: databaseProvision.readwriteRole,
2123
- EBK_RDS_CA_BUNDLE: '/app/rds-global-bundle.pem',
2124
- EBK_READWRITE_SECRET_ARN: databaseProvision.readwriteSecretArn,
2125
- EBK_SLICE: '${name}',
2126
- EBK_STAGE: $app.stage,
2127
- EBK_WORKLOAD_IDENTITY: '${name}:data-retention-job',
2128
- EBK_WORKSPACE: '${workspace}',
2129
- EVENT_BUS_NAME: foundation.eventBus.name,
2130
- },
2131
- image: dataRetentionContainer.image,
2132
- link: secretLinksFor('data-retention-job-task'),
2133
- logging: {
2134
- name: retentionTaskLogs.name,
2135
- retention: '1 month',
2136
- },
2137
- transform: {
2138
- logGroup: {
2139
- logGroupClass: retentionTaskLogs.logGroupClass,
2140
- tags: retentionTaskLogs.tags,
2141
- },
2142
- taskDefinition: {
2143
- family: workloadRoleResourceNames!.dataRetentionTaskFamily,
2144
- },
2145
- },
2146
- });
2147
- const retentionTaskLogDataProtection = managedLogDataProtection(
2148
- '${pascalName}DataRetentionTaskLog',
2149
- retentionTaskLogs,
2150
- retentionTaskLogs.name,
2151
- [retentionTask.nodes.taskDefinition],
2152
- foundation.discovery.alertTopicArn,
2153
- );
2154
- const retentionWorkflowLogs = managedLogGroup(
2155
- '${pascalName}DataRetentionWorkflowLogs',
2156
- 'job-data-retention-orchestration',
2157
- foundation.discovery.alertTopicArn,
2158
- );
2159
- const retentionOverrides = retentionTask.containers.apply(
2160
- (containers: string[]) => ({
2161
- ContainerOverrides: containers.map((containerName) => ({
2162
- Environment: [
2163
- {
2164
- Name: 'EBK_RETENTION_APPROVAL_ID',
2165
- Value: '{% $states.input.approvalRequestId %}',
2166
- },
2167
- {
2168
- Name: 'EBK_RETENTION_CORRELATION_ID',
2169
- Value: '{% $states.input.correlationId %}',
2170
- },
2171
- {
2172
- Name: 'EBK_RETENTION_AS_OF',
2173
- Value: '{% $states.input.asOf %}',
2174
- },
2175
- {
2176
- Name: 'EBK_RETENTION_MODE',
2177
- Value: '{% $states.input.mode %}',
2178
- },
2179
- {
2180
- Name: 'EBK_RETENTION_RUN_ID',
2181
- Value: '{% $states.input.runId %}',
2182
- },
2183
- {
2184
- Name: 'EBK_STEP_FUNCTIONS_TASK_TOKEN',
2185
- Value: '{% $states.context.Task.Token %}',
2186
- },
2187
- ],
2188
- Name: containerName,
2189
- })),
2190
- }),
2191
- );
2192
- const retentionStateMachine = createRawStateMachine(
2193
- '${pascalName}DataRetention',
2194
- {
2195
- StartAt: 'RunDataRetention',
2196
- States: {
2197
- DataRetentionSucceeded: {
2198
- QueryLanguage: 'JSONata',
2199
- Type: 'Succeed',
2200
- },
2201
- RunDataRetention: {
2202
- Arguments: {
2203
- ...taskArguments(retentionTask),
2204
- Overrides: retentionOverrides,
2205
- },
2206
- Next: 'DataRetentionSucceeded',
2207
- QueryLanguage: 'JSONata',
2208
- Resource: 'arn:aws:states:::ecs:runTask.waitForTaskToken',
2209
- Retry: [
2210
- {
2211
- BackoffRate: 2,
2212
- ErrorEquals: [
2213
- 'ECS.AmazonECSException',
2214
- 'ECS.ECSException',
2215
- ],
2216
- IntervalSeconds: 10,
2217
- MaxAttempts: 2,
2218
- },
2219
- ],
2220
- TimeoutSeconds: 2700,
2221
- Type: 'Task',
2222
- },
2223
- },
2224
- },
2225
- retentionWorkflowLogs.logGroup,
2226
- workloadRoleBindings!.dataRetention.orchestration.roleArn,
2227
- );
2228
- const retentionFailureAlarm = new aws.cloudwatch.MetricAlarm(
2229
- '${pascalName}DataRetentionFailure',
2230
- {
2231
- alarmActions: [foundation.discovery.alertTopicArn],
2232
- alarmDescription: operationalAlarmDescription({
2233
- businessSymptom:
2234
- 'The built-in retention Job terminated unsuccessfully.',
2235
- id: 'retention-job-failure',
2236
- runbook: 'docs/runbooks/database-recovery.md',
2237
- }),
2238
- comparisonOperator: 'GreaterThanOrEqualToThreshold',
2239
- evaluationPeriods: 1,
2240
- metricQueries: [
2241
- ...['ExecutionsFailed', 'ExecutionsTimedOut', 'ExecutionsAborted'].map(
2242
- (metricName, index) => ({
2243
- id: 'm' + index,
2244
- metric: {
2245
- dimensions: {
2246
- StateMachineArn: retentionStateMachine.arn,
2247
- },
2248
- metricName,
2249
- namespace: 'AWS/States',
2250
- period: 60,
2251
- stat: 'Sum',
2252
- },
2253
- returnData: false,
2254
- }),
2255
- ),
2256
- {
2257
- expression: 'm0+m1+m2',
2258
- id: 'failures',
2259
- label: 'Retention terminal execution failures',
2260
- returnData: true,
2261
- },
2262
- ],
2263
- name: '${name}-' + $app.stage + '-retention-job-failure',
2264
- threshold: 1,
2265
- treatMissingData: 'notBreaching',
2266
- },
2267
- );
2268
- const retentionRegistration = registerProductRetention(
2269
- retentionStateMachine.arn,
2270
- );
2271
- migrationControls.dataRetention = {
2272
- registration: retentionRegistration,
2273
- retentionFailureAlarm,
2274
- retentionStateMachine,
2275
- retentionTask,
2276
- retentionTaskLogDataProtection,
2277
- retentionWorkflowLogs,
2278
- };
2279
- const retentionMaintenanceTaskLogs = managedLogContract(
2280
- 'job-data-retention-maintenance',
2281
- );
2282
- const retentionMaintenanceTask = new sst.aws.Task(
2283
- '${pascalName}DataRetentionMaintenanceTask',
2284
- {
2285
- ...workloadRoleBindings!.dataRetention.maintenance.task,
2286
- cluster: foundation.cluster,
2287
- environment: {
2288
- ...dataRetentionContainer.environment,
2289
- AWS_REGION: region,
2290
- EBK_DATABASE_HOST: foundation.database.clusterEndpoint,
2291
- EBK_DATABASE_NAME: databaseProvision.databaseName,
2292
- EBK_DATABASE_PORT: $interpolate\`\${foundation.database.port}\`,
2293
- EBK_DATABASE_USERNAME: databaseProvision.readwriteRole,
2294
- EBK_RDS_CA_BUNDLE: '/app/rds-global-bundle.pem',
2295
- EBK_READWRITE_SECRET_ARN:
2296
- databaseProvision.readwriteSecretArn,
2297
- EBK_SLICE: '${name}',
2298
- EBK_STAGE: $app.stage,
2299
- EBK_WORKLOAD_IDENTITY: '${name}:data-retention-job',
2300
- EBK_WORKSPACE: '${workspace}',
2301
- },
2302
- image: dataRetentionContainer.image,
2303
- link: secretLinksFor(
2304
- 'data-retention-maintenance-job-task',
2305
- ),
2306
- logging: {
2307
- name: retentionMaintenanceTaskLogs.name,
2308
- retention: '1 month',
2309
- },
2310
- transform: {
2311
- logGroup: {
2312
- logGroupClass:
2313
- retentionMaintenanceTaskLogs.logGroupClass,
2314
- tags: retentionMaintenanceTaskLogs.tags,
2315
- },
2316
- taskDefinition: {
2317
- family:
2318
- workloadRoleResourceNames!
2319
- .dataRetentionMaintenanceTaskFamily,
2320
- },
2321
- },
2322
- },
2323
- );
2324
- const retentionMaintenanceTaskLogDataProtection =
2325
- managedLogDataProtection(
2326
- '${pascalName}DataRetentionMaintenanceTaskLog',
2327
- retentionMaintenanceTaskLogs,
2328
- retentionMaintenanceTaskLogs.name,
2329
- [retentionMaintenanceTask.nodes.taskDefinition],
2330
- foundation.discovery.alertTopicArn,
2331
- );
2332
- const retentionMaintenanceLauncherLogs = managedLogGroup(
2333
- '${pascalName}DataRetentionMaintenanceLauncherLogs',
2334
- 'job-data-retention-maintenance-launcher',
2335
- foundation.discovery.alertTopicArn,
2336
- );
2337
- const retentionMaintenanceContainerName =
2338
- retentionMaintenanceTask.containers.apply(
2339
- (containers: string[]) => {
2340
- if (containers.length !== 1 || !containers[0]) {
2341
- throw new Error(
2342
- 'Retention maintenance requires exactly one task container.',
2343
- );
2344
- }
2345
- return containers[0];
2346
- },
2347
- );
2348
- const retentionMaintenanceLauncher = new sst.aws.Function(
2349
- '${pascalName}DataRetentionMaintenanceLauncher',
2350
- {
2351
- ...workloadRoleBindings!.dataRetention.maintenance.launcher,
2352
- environment: {
2353
- EBK_MAINTENANCE_ASSIGN_PUBLIC_IP:
2354
- retentionMaintenanceTask.assignPublicIp.apply(
2355
- (value: boolean) => String(value),
2356
- ),
2357
- EBK_MAINTENANCE_CLUSTER_ARN:
2358
- retentionMaintenanceTask.cluster,
2359
- EBK_MAINTENANCE_CONTAINER_NAME:
2360
- retentionMaintenanceContainerName,
2361
- EBK_MAINTENANCE_SECURITY_GROUP_IDS: $jsonStringify(
2362
- retentionMaintenanceTask.securityGroups,
2363
- ),
2364
- EBK_MAINTENANCE_SUBNET_IDS: $jsonStringify(
2365
- retentionMaintenanceTask.subnets,
2366
- ),
2367
- EBK_MAINTENANCE_TASK_DEFINITION_ARN:
2368
- retentionMaintenanceTask.taskDefinition,
2369
- EBK_STAGE: $app.stage,
2370
- EBK_WORKSPACE: '${workspace}',
2371
- },
2372
- handler: 'jobs/data-retention-maintenance-launcher.handler',
2373
- ...lambdaLogging(
2374
- retentionMaintenanceLauncherLogs.logGroup.name,
2375
- ),
2376
- timeout: '1 minute',
2377
- },
2378
- );
2379
- const retentionMaintenanceRule = new aws.cloudwatch.EventRule(
2380
- '${pascalName}DataRetentionMaintenanceSchedule',
2381
- {
2382
- name:
2383
- workloadRoleResourceNames!.dataRetentionMaintenanceRule,
2384
- scheduleExpression: 'rate(1 day)',
2385
- },
2386
- );
2387
- const retentionMaintenancePermission =
2388
- new aws.lambda.Permission(
2389
- '${pascalName}DataRetentionMaintenanceSchedulePermission',
2390
- {
2391
- action: 'lambda:InvokeFunction',
2392
- function: retentionMaintenanceLauncher.arn,
2393
- principal: 'events.amazonaws.com',
2394
- sourceArn: retentionMaintenanceRule.arn,
2395
- },
2396
- );
2397
- const retentionMaintenanceTarget =
2398
- new aws.cloudwatch.EventTarget(
2399
- '${pascalName}DataRetentionMaintenanceTarget',
2400
- {
2401
- arn: retentionMaintenanceLauncher.arn,
2402
- rule: retentionMaintenanceRule.name,
2403
- targetId: 'DataRetentionMaintenance',
2404
- },
2405
- { dependsOn: [retentionMaintenancePermission] },
2406
- );
2407
- migrationControls.dataRetentionMaintenance = {
2408
- retentionMaintenanceLauncher,
2409
- retentionMaintenanceLauncherLogs,
2410
- retentionMaintenanceRule,
2411
- retentionMaintenanceTarget,
2412
- retentionMaintenanceTask,
2413
- retentionMaintenanceTaskLogDataProtection,
2414
2611
  };
2415
2612
 
2613
+ ${''}\
2416
2614
  for (const [databaseIdentifier, target] of Object.entries(
2417
2615
  databaseProvisions.databases,
2418
2616
  )) {
@@ -2434,12 +2632,12 @@ export function product(workloadRoles?: StandardSliceWorkloadRolePreflight) {
2434
2632
  'database-retirement',
2435
2633
  databaseIdentifier,
2436
2634
  );
2437
- const databaseRoleBindings =
2438
- workloadRoleBindings!.database(databaseIdentifier);
2439
2635
  const bootstrapTask = new sst.aws.Task(
2440
2636
  target.resourceName + 'BootstrapTask',
2441
2637
  {
2442
- ...databaseRoleBindings.bootstrap,
2638
+ permissions: nativeAuthority.permissions(
2639
+ 'database-' + databaseIdentifier + '-bootstrap-task',
2640
+ ),
2443
2641
  cluster: foundation.cluster,
2444
2642
  command: ['node', 'jobs/bootstrap-database.mjs'],
2445
2643
  environment: {
@@ -2496,7 +2694,9 @@ export function product(workloadRoles?: StandardSliceWorkloadRolePreflight) {
2496
2694
  const migrationTask = new sst.aws.Task(
2497
2695
  target.resourceName + 'MigrationTask',
2498
2696
  {
2499
- ...databaseRoleBindings.migration,
2697
+ permissions: nativeAuthority.permissions(
2698
+ 'database-' + databaseIdentifier + '-migration-task',
2699
+ ),
2500
2700
  cluster: foundation.cluster,
2501
2701
  environment: {
2502
2702
  ...databaseMigrationContainer.environment,
@@ -2562,6 +2762,18 @@ export function product(workloadRoles?: StandardSliceWorkloadRolePreflight) {
2562
2762
  Next: 'RunDatabaseMigration',
2563
2763
  QueryLanguage: 'JSONata',
2564
2764
  Resource: 'arn:aws:states:::ecs:runTask.sync',
2765
+ // The first bootstrap of a Slice runs minutes after its execution
2766
+ // role is created; ECR can still answer "not found" to the fresh
2767
+ // role and the task never starts (EBK-INT-2026-072). Bootstrap is
2768
+ // idempotent, so a bounded retry is safe.
2769
+ Retry: [
2770
+ {
2771
+ BackoffRate: 2,
2772
+ ErrorEquals: ['States.TaskFailed'],
2773
+ IntervalSeconds: 30,
2774
+ MaxAttempts: 3,
2775
+ },
2776
+ ],
2565
2777
  TimeoutSeconds: 2700,
2566
2778
  Type: 'Task',
2567
2779
  },
@@ -2583,7 +2795,7 @@ export function product(workloadRoles?: StandardSliceWorkloadRolePreflight) {
2583
2795
  },
2584
2796
  },
2585
2797
  migrationWorkflowLogs.logGroup,
2586
- databaseRoleBindings.migrationOrchestration.roleArn,
2798
+ 'database-' + databaseIdentifier + '-migration-orchestration',
2587
2799
  );
2588
2800
  const registration = registerProductMigration(
2589
2801
  target.resourceName + 'MigrationRegistration',
@@ -2592,10 +2804,11 @@ export function product(workloadRoles?: StandardSliceWorkloadRolePreflight) {
2592
2804
  );
2593
2805
  const retirementTask =
2594
2806
  $app.stage === 'development' &&
2595
- databaseIdentifier === 'retirement_rehearsal' &&
2596
- databaseRoleBindings.retirement
2807
+ databaseIdentifier === 'retirement_rehearsal'
2597
2808
  ? new sst.aws.Task(target.resourceName + 'RetirementTask', {
2598
- ...databaseRoleBindings.retirement.task,
2809
+ permissions: nativeAuthority.permissions(
2810
+ 'database-' + databaseIdentifier + '-retirement-task',
2811
+ ),
2599
2812
  cluster: foundation.cluster,
2600
2813
  command: ['node', 'jobs/retire-database.mjs'],
2601
2814
  environment: {
@@ -2709,7 +2922,7 @@ export function product(workloadRoles?: StandardSliceWorkloadRolePreflight) {
2709
2922
  },
2710
2923
  },
2711
2924
  retirementWorkflowLogs.logGroup,
2712
- databaseRoleBindings.retirement!.orchestration.roleArn,
2925
+ 'database-' + databaseIdentifier + '-retirement-orchestration',
2713
2926
  );
2714
2927
  retirementControl = {
2715
2928
  retirementWorkflowLogs,
@@ -2731,12 +2944,15 @@ export function product(workloadRoles?: StandardSliceWorkloadRolePreflight) {
2731
2944
  }
2732
2945
  }
2733
2946
 
2947
+ `
2948
+ : ''}\
2734
2949
  const outboxRelayAlarm = new aws.cloudwatch.MetricAlarm(
2735
2950
  '${pascalName}OutboxRelayErrors',
2736
2951
  {
2737
2952
  alarmActions: [foundation.discovery.alertTopicArn],
2738
2953
  alarmDescription: operationalAlarmDescription({
2739
- businessSymptom: 'The transactional outbox relay is failing.',
2954
+ businessSymptom:
2955
+ 'The isolated performance-sink outbox relay is failing.',
2740
2956
  id: 'outbox-relay-errors',
2741
2957
  runbook: 'docs/runbooks/async-recovery.md',
2742
2958
  }),
@@ -2821,27 +3037,6 @@ export function product(workloadRoles?: StandardSliceWorkloadRolePreflight) {
2821
3037
  treatMissingData: 'breaching',
2822
3038
  },
2823
3039
  );
2824
- const outboxBacklogAlarm = new aws.cloudwatch.MetricAlarm(
2825
- '${pascalName}OutboxBacklog',
2826
- {
2827
- alarmActions: [foundation.discovery.alertTopicArn],
2828
- alarmDescription: operationalAlarmDescription({
2829
- businessSymptom: 'Transactional outbox work is accumulating.',
2830
- id: 'outbox-backlog',
2831
- runbook: 'docs/runbooks/async-recovery.md',
2832
- }),
2833
- comparisonOperator: 'GreaterThanOrEqualToThreshold',
2834
- dimensions: { Slice: '${name}', Stage: $app.stage },
2835
- evaluationPeriods: 2,
2836
- metricName: 'OutboxBacklog',
2837
- name: '${name}-' + $app.stage + '-outbox-backlog',
2838
- namespace: 'EBK/Async',
2839
- period: 300,
2840
- statistic: 'Maximum',
2841
- threshold: 1,
2842
- treatMissingData: 'notBreaching',
2843
- },
2844
- );
2845
3040
  const inboxCleanupBacklogAlarm = new aws.cloudwatch.MetricAlarm(
2846
3041
  '${pascalName}InboxCleanupBacklog',
2847
3042
  {
@@ -2987,6 +3182,7 @@ export function product(workloadRoles?: StandardSliceWorkloadRolePreflight) {
2987
3182
  performanceLoadCleanupBacklogAlarm,
2988
3183
  performanceLoadCleanupDeadlineAlarm,
2989
3184
  asyncOutboxRelay,
3185
+ ${sql ? ' sqlOutbox,\n sqlOutboxAttemptsAlarm,\n sqlOutboxBacklogAlarm,\n sqlOutboxRelayErrorAlarm,\n' : ''}\
2990
3186
  asyncRecoveryLedger,
2991
3187
  asyncScheduleTarget,
2992
3188
  asyncTargetDlq,
@@ -3006,12 +3202,18 @@ export function product(workloadRoles?: StandardSliceWorkloadRolePreflight) {
3006
3202
  dataExportCleanupRule,
3007
3203
  dataExportCleanupTarget,
3008
3204
  dataExportStore,
3009
- database,
3205
+ ${sql ? ' database,\n' : ''}\
3206
+ ${' keyValue,\n'}\
3010
3207
  dlqAlarm,
3011
3208
  eventDlq,
3012
3209
  eventQueue,
3013
3210
  machineClientProvisioner,
3014
3211
  migrationControls,
3212
+ eventDeliveryAttemptsAlarm,
3213
+ eventIntentRejectedAlarm,
3214
+ keyValueReconciliationErrorAlarm,
3215
+ keyValueStreamDeliveryErrorAlarm,
3216
+ keyValueStreamIteratorAgeAlarm,
3015
3217
  outboxBacklogAlarm,
3016
3218
  outboxRelayAlarm,
3017
3219
  outboxRelayRule,
@@ -3034,5 +3236,7 @@ export function product(workloadRoles?: StandardSliceWorkloadRolePreflight) {
3034
3236
  }
3035
3237
  `,
3036
3238
  };
3239
+ Object.assign(files, (0, built_in_plan_key_value_retention_infrastructure_js_1.renderKeyValueRetentionInfrastructureFiles)(name, pascalName));
3240
+ return files;
3037
3241
  }
3038
3242
  //# sourceMappingURL=built-in-plan-infrastructure-files.js.map