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

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 (261) hide show
  1. package/executors.json +8 -3
  2. package/package.json +10 -7
  3. package/src/aws/child-environment.d.ts +10 -0
  4. package/src/aws/child-environment.js +77 -0
  5. package/src/aws/child-environment.js.map +1 -0
  6. package/src/aws/identity.d.ts +13 -0
  7. package/src/aws/identity.js +41 -0
  8. package/src/aws/identity.js.map +1 -0
  9. package/src/aws-authority/index.d.ts +1 -0
  10. package/src/aws-authority/index.js +5 -0
  11. package/src/aws-authority/index.js.map +1 -0
  12. package/src/aws-authority/stage-authority.d.ts +238 -0
  13. package/src/aws-authority/stage-authority.js +2836 -0
  14. package/src/aws-authority/stage-authority.js.map +1 -0
  15. package/src/developer-agent/generator-planning.js +4 -4
  16. package/src/developer-agent/generator-planning.js.map +1 -1
  17. package/src/developer-agent/operation-registry.js +14 -6
  18. package/src/developer-agent/operation-registry.js.map +1 -1
  19. package/src/executors/deployment-plan/deployment-plan.js +1 -1
  20. package/src/executors/deployment-plan/deployment-plan.js.map +1 -1
  21. package/src/executors/foundation-preflight/foundation-preflight.d.ts +0 -1
  22. package/src/executors/foundation-preflight/foundation-preflight.js.map +1 -1
  23. package/src/executors/foundation-preflight/schema.json +0 -5
  24. package/src/executors/foundation-profile-preflight/foundation-profile-preflight.d.ts +12 -3
  25. package/src/executors/foundation-profile-preflight/foundation-profile-preflight.js +95 -32
  26. package/src/executors/foundation-profile-preflight/foundation-profile-preflight.js.map +1 -1
  27. package/src/executors/foundation-profile-preflight/production-database-safeguard.d.ts +64 -0
  28. package/src/executors/foundation-profile-preflight/production-database-safeguard.js +221 -0
  29. package/src/executors/foundation-profile-preflight/production-database-safeguard.js.map +1 -0
  30. package/src/executors/foundation-profile-preflight/schema.json +1 -6
  31. package/src/executors/foundation-remove-preflight/foundation-remove-preflight.d.ts +11 -1
  32. package/src/executors/foundation-remove-preflight/foundation-remove-preflight.js +44 -21
  33. package/src/executors/foundation-remove-preflight/foundation-remove-preflight.js.map +1 -1
  34. package/src/executors/foundation-remove-preflight/schema.json +6 -1
  35. package/src/executors/infrastructure-plan/infrastructure-plan.d.ts +25 -369
  36. package/src/executors/infrastructure-plan/infrastructure-plan.js +222 -5472
  37. package/src/executors/infrastructure-plan/infrastructure-plan.js.map +1 -1
  38. package/src/executors/infrastructure-plan/schema.json +8 -64
  39. package/src/executors/local-deploy/federated-credential-transport.d.ts +48 -0
  40. package/src/executors/local-deploy/federated-credential-transport.js +252 -0
  41. package/src/executors/local-deploy/federated-credential-transport.js.map +1 -0
  42. package/src/executors/local-deploy/local-approval.d.ts +4 -9
  43. package/src/executors/local-deploy/local-approval.js +3 -7
  44. package/src/executors/local-deploy/local-approval.js.map +1 -1
  45. package/src/executors/local-deploy/local-deploy.d.ts +58 -43
  46. package/src/executors/local-deploy/local-deploy.js +938 -804
  47. package/src/executors/local-deploy/local-deploy.js.map +1 -1
  48. package/src/executors/local-deploy/schema.json +3 -20
  49. package/src/executors/refresh/refresh.js +2 -2
  50. package/src/executors/refresh/refresh.js.map +1 -1
  51. package/src/executors/remove/remove.js +2 -2
  52. package/src/executors/remove/remove.js.map +1 -1
  53. package/src/executors/router-waf-rehearsal/router-waf-rehearsal.d.ts +25 -1
  54. package/src/executors/router-waf-rehearsal/router-waf-rehearsal.js +905 -22
  55. package/src/executors/router-waf-rehearsal/router-waf-rehearsal.js.map +1 -1
  56. package/src/executors/router-waf-rehearsal/schema.json +17 -2
  57. package/src/executors/secret-initialize/schema.json +42 -0
  58. package/src/executors/secret-initialize/secret-initialize.d.ts +30 -0
  59. package/src/executors/secret-initialize/secret-initialize.js +229 -0
  60. package/src/executors/secret-initialize/secret-initialize.js.map +1 -0
  61. package/src/executors/secret-metadata/secret-metadata.d.ts +2 -1
  62. package/src/executors/secret-metadata/secret-metadata.js +62 -29
  63. package/src/executors/secret-metadata/secret-metadata.js.map +1 -1
  64. package/src/executors/sst-lifecycle/generated-secret-retirement.d.ts +53 -0
  65. package/src/executors/sst-lifecycle/generated-secret-retirement.js +192 -0
  66. package/src/executors/sst-lifecycle/generated-secret-retirement.js.map +1 -0
  67. package/src/executors/sst-lifecycle/governed-native-operation.d.ts +46 -0
  68. package/src/executors/sst-lifecycle/governed-native-operation.js +153 -0
  69. package/src/executors/sst-lifecycle/governed-native-operation.js.map +1 -0
  70. package/src/executors/sst-lifecycle/run-sst-command.d.ts +25 -7
  71. package/src/executors/sst-lifecycle/run-sst-command.js +459 -89
  72. package/src/executors/sst-lifecycle/run-sst-command.js.map +1 -1
  73. package/src/executors/sst-lifecycle/schema.json +0 -5
  74. package/src/executors/sst-lifecycle/secret-preflight.d.ts +2 -0
  75. package/src/executors/sst-lifecycle/secret-preflight.js +13 -7
  76. package/src/executors/sst-lifecycle/secret-preflight.js.map +1 -1
  77. package/src/executors/unlock/unlock.js +2 -2
  78. package/src/executors/unlock/unlock.js.map +1 -1
  79. package/src/foundation/router-waf-rate-rehearsal.d.ts +97 -0
  80. package/src/foundation/router-waf-rate-rehearsal.js +210 -0
  81. package/src/foundation/router-waf-rate-rehearsal.js.map +1 -0
  82. package/src/foundation/router-waf.d.ts +59 -9
  83. package/src/foundation/router-waf.js +427 -32
  84. package/src/foundation/router-waf.js.map +1 -1
  85. package/src/foundation/stages.d.ts +1 -0
  86. package/src/foundation/stages.js +11 -0
  87. package/src/foundation/stages.js.map +1 -1
  88. package/src/generators/formatting.d.ts +8 -0
  89. package/src/generators/formatting.js +118 -0
  90. package/src/generators/formatting.js.map +1 -0
  91. package/src/generators/function/function.js +37 -17
  92. package/src/generators/function/function.js.map +1 -1
  93. package/src/generators/post-generation-install.js +2 -2
  94. package/src/generators/post-generation-install.js.map +1 -1
  95. package/src/generators/preset/generator.js +22 -7
  96. package/src/generators/preset/generator.js.map +1 -1
  97. package/src/generators/preset/workspace-files-developer-agent.js +15 -7
  98. package/src/generators/preset/workspace-files-developer-agent.js.map +1 -1
  99. package/src/generators/preset/workspace-files-ecr-inspector.d.ts +18 -0
  100. package/src/generators/preset/workspace-files-ecr-inspector.js +298 -0
  101. package/src/generators/preset/workspace-files-ecr-inspector.js.map +1 -0
  102. package/src/generators/preset/workspace-files-performance-cost.d.ts +2 -2
  103. package/src/generators/preset/workspace-files-performance-cost.js +8 -8
  104. package/src/generators/preset/workspace-files-performance-cost.js.map +1 -1
  105. package/src/generators/preset/workspace-files-performance-load.d.ts +1 -1
  106. package/src/generators/preset/workspace-files-performance-load.js +8 -12
  107. package/src/generators/preset/workspace-files-performance-load.js.map +1 -1
  108. package/src/generators/preset/workspace-files-stage-authority.d.ts +4 -0
  109. package/src/generators/preset/workspace-files-stage-authority.js +275 -0
  110. package/src/generators/preset/workspace-files-stage-authority.js.map +1 -0
  111. package/src/generators/preset/workspace-files-thin-deploy.d.ts +35 -0
  112. package/src/generators/preset/workspace-files-thin-deploy.js +1434 -0
  113. package/src/generators/preset/workspace-files-thin-deploy.js.map +1 -0
  114. package/src/generators/preset/workspace-files-thin-docs.d.ts +5 -0
  115. package/src/generators/preset/workspace-files-thin-docs.js +224 -0
  116. package/src/generators/preset/workspace-files-thin-docs.js.map +1 -0
  117. package/src/generators/preset/workspace-files-thin-release.d.ts +14 -0
  118. package/src/generators/preset/workspace-files-thin-release.js +26 -0
  119. package/src/generators/preset/workspace-files-thin-release.js.map +1 -0
  120. package/src/generators/preset/workspace-files.d.ts +1 -1
  121. package/src/generators/preset/workspace-files.js +373 -223
  122. package/src/generators/preset/workspace-files.js.map +1 -1
  123. package/src/generators/slice/built-in-plan-api-files.js +1 -0
  124. package/src/generators/slice/built-in-plan-api-files.js.map +1 -1
  125. package/src/generators/slice/built-in-plan-authorization-infrastructure-files.js +4 -0
  126. package/src/generators/slice/built-in-plan-authorization-infrastructure-files.js.map +1 -1
  127. package/src/generators/slice/built-in-plan-data-export-infrastructure-files.js +5 -31
  128. package/src/generators/slice/built-in-plan-data-export-infrastructure-files.js.map +1 -1
  129. package/src/generators/slice/built-in-plan-data-files.d.ts +1 -1
  130. package/src/generators/slice/built-in-plan-data-files.js +89 -30
  131. package/src/generators/slice/built-in-plan-data-files.js.map +1 -1
  132. package/src/generators/slice/built-in-plan-foundation-files.js +3 -5
  133. package/src/generators/slice/built-in-plan-foundation-files.js.map +1 -1
  134. package/src/generators/slice/built-in-plan-frontend-files.js +5 -1
  135. package/src/generators/slice/built-in-plan-frontend-files.js.map +1 -1
  136. package/src/generators/slice/built-in-plan-infrastructure-files.d.ts +1 -1
  137. package/src/generators/slice/built-in-plan-infrastructure-files.js +199 -188
  138. package/src/generators/slice/built-in-plan-infrastructure-files.js.map +1 -1
  139. package/src/generators/slice/built-in-plan-metadata-files.js +13 -0
  140. package/src/generators/slice/built-in-plan-metadata-files.js.map +1 -1
  141. package/src/generators/slice/built-in-plan-native-runtime-authority-files.d.ts +2 -0
  142. package/src/generators/slice/built-in-plan-native-runtime-authority-files.js +307 -0
  143. package/src/generators/slice/built-in-plan-native-runtime-authority-files.js.map +1 -0
  144. package/src/generators/slice/built-in-plan-operations-files.js +43 -49
  145. package/src/generators/slice/built-in-plan-operations-files.js.map +1 -1
  146. package/src/generators/slice/built-in-plan-performance-files.js +37 -19
  147. package/src/generators/slice/built-in-plan-performance-files.js.map +1 -1
  148. package/src/generators/slice/built-in-plan-project-files.js +9 -68
  149. package/src/generators/slice/built-in-plan-project-files.js.map +1 -1
  150. package/src/generators/slice/built-in-plan-secret-runtime-files.d.ts +1 -1
  151. package/src/generators/slice/built-in-plan-secret-runtime-files.js +74 -76
  152. package/src/generators/slice/built-in-plan-secret-runtime-files.js.map +1 -1
  153. package/src/generators/slice/built-in-plan-testing-files.js +74 -47
  154. package/src/generators/slice/built-in-plan-testing-files.js.map +1 -1
  155. package/src/generators/slice/built-in-plan-thin-release-files.d.ts +11 -0
  156. package/src/generators/slice/built-in-plan-thin-release-files.js +129 -0
  157. package/src/generators/slice/built-in-plan-thin-release-files.js.map +1 -0
  158. package/src/generators/slice/built-in-plan-verification-files.js +6 -2
  159. package/src/generators/slice/built-in-plan-verification-files.js.map +1 -1
  160. package/src/generators/slice/built-in-plan-web-files.js +4 -2
  161. package/src/generators/slice/built-in-plan-web-files.js.map +1 -1
  162. package/src/generators/slice/built-in-plan-workload-role-files.js +26 -4
  163. package/src/generators/slice/built-in-plan-workload-role-files.js.map +1 -1
  164. package/src/generators/slice/built-in-plan.d.ts +1 -0
  165. package/src/generators/slice/built-in-plan.js +28 -11
  166. package/src/generators/slice/built-in-plan.js.map +1 -1
  167. package/src/generators/slice/slice.js +27 -29
  168. package/src/generators/slice/slice.js.map +1 -1
  169. package/src/generators/upgrade/upgrade.d.ts +1 -1
  170. package/src/generators/upgrade/upgrade.js +224 -69
  171. package/src/generators/upgrade/upgrade.js.map +1 -1
  172. package/src/generators/workload/performance-registration.d.ts +1 -0
  173. package/src/generators/workload/performance-registration.js +6 -4
  174. package/src/generators/workload/performance-registration.js.map +1 -1
  175. package/src/generators/workload/service-performance-files.d.ts +1 -0
  176. package/src/generators/workload/service-performance-files.js +27 -7
  177. package/src/generators/workload/service-performance-files.js.map +1 -1
  178. package/src/generators/workload/workload.d.ts +1 -1
  179. package/src/generators/workload/workload.js +55 -28
  180. package/src/generators/workload/workload.js.map +1 -1
  181. package/src/git-policy/github-bootstrap.d.ts +19 -6
  182. package/src/git-policy/github-bootstrap.js +103 -157
  183. package/src/git-policy/github-bootstrap.js.map +1 -1
  184. package/src/local-dev/private-state.d.ts +3 -0
  185. package/src/local-dev/private-state.js +17 -5
  186. package/src/local-dev/private-state.js.map +1 -1
  187. package/src/performance/cost-collection.d.ts +1 -1
  188. package/src/performance/cost-collection.js +4 -2
  189. package/src/performance/cost-collection.js.map +1 -1
  190. package/src/performance/cost-controls.d.ts +3 -1
  191. package/src/performance/cost-controls.js +9 -4
  192. package/src/performance/cost-controls.js.map +1 -1
  193. package/src/performance/evidence.d.ts +1 -1
  194. package/src/performance/evidence.js +5 -3
  195. package/src/performance/evidence.js.map +1 -1
  196. package/src/performance/load-safety.js +5 -3
  197. package/src/performance/load-safety.js.map +1 -1
  198. package/src/performance/model.d.ts +2 -2
  199. package/src/performance/model.js +4 -3
  200. package/src/performance/model.js.map +1 -1
  201. package/src/release/index.d.ts +0 -2
  202. package/src/release/index.js +0 -2
  203. package/src/release/index.js.map +1 -1
  204. package/src/release/source-authority.d.ts +4 -5
  205. package/src/release/source-authority.js +13 -123
  206. package/src/release/source-authority.js.map +1 -1
  207. package/src/repository-policy/aurora-preflight.d.ts +1 -1
  208. package/src/repository-policy/aurora-preflight.js +4 -1
  209. package/src/repository-policy/aurora-preflight.js.map +1 -1
  210. package/src/repository-policy/policy.d.ts +5 -1
  211. package/src/repository-policy/policy.js +64 -13
  212. package/src/repository-policy/policy.js.map +1 -1
  213. package/src/schemas/performance-policy-v1.json +3 -1
  214. package/src/secrets/adapters.d.ts +5 -1
  215. package/src/secrets/adapters.js +22 -1
  216. package/src/secrets/adapters.js.map +1 -1
  217. package/src/secrets/aws-control-plane.d.ts +10 -1
  218. package/src/secrets/aws-control-plane.js +67 -0
  219. package/src/secrets/aws-control-plane.js.map +1 -1
  220. package/src/secrets/generated-aws-control-plane.d.ts +23 -0
  221. package/src/secrets/generated-aws-control-plane.js +259 -0
  222. package/src/secrets/generated-aws-control-plane.js.map +1 -0
  223. package/src/secrets/metadata.js +38 -10
  224. package/src/secrets/metadata.js.map +1 -1
  225. package/src/secrets/model.d.ts +8 -1
  226. package/src/secrets/model.js +24 -3
  227. package/src/secrets/model.js.map +1 -1
  228. package/src/secrets/operator.d.ts +89 -3
  229. package/src/secrets/operator.js +422 -9
  230. package/src/secrets/operator.js.map +1 -1
  231. package/src/secrets/registry.d.ts +2 -1
  232. package/src/secrets/registry.js +18 -1
  233. package/src/secrets/registry.js.map +1 -1
  234. package/src/secrets/workload-access.js +10 -2
  235. package/src/secrets/workload-access.js.map +1 -1
  236. package/src/supply-chain/action-pins.js +61 -7
  237. package/src/supply-chain/action-pins.js.map +1 -1
  238. package/src/supply-chain/build-output-bounds.d.ts +2 -0
  239. package/src/supply-chain/build-output-bounds.js +12 -0
  240. package/src/supply-chain/build-output-bounds.js.map +1 -0
  241. package/src/supply-chain/build-output.js +2 -4
  242. package/src/supply-chain/build-output.js.map +1 -1
  243. package/src/supply-chain/ecr-scanning.d.ts +5 -3
  244. package/src/supply-chain/ecr-scanning.js +11 -7
  245. package/src/supply-chain/ecr-scanning.js.map +1 -1
  246. package/src/supply-chain/ecs-deployed-image.d.ts +5 -3
  247. package/src/supply-chain/ecs-deployed-image.js +6 -4
  248. package/src/supply-chain/ecs-deployed-image.js.map +1 -1
  249. package/src/supply-chain/generated-container-release-command.js +816 -34
  250. package/src/supply-chain/generated-container-release-command.js.map +1 -1
  251. package/src/supply-chain/generated-ecr-collector-command.d.ts +0 -1
  252. package/src/supply-chain/generated-ecr-collector-command.js +188 -91
  253. package/src/supply-chain/generated-ecr-collector-command.js.map +1 -1
  254. package/src/supply-chain/generated-files.js +4 -3
  255. package/src/supply-chain/generated-files.js.map +1 -1
  256. package/src/supply-chain/index.d.ts +1 -1
  257. package/src/supply-chain/index.js +2 -1
  258. package/src/supply-chain/index.js.map +1 -1
  259. package/src/workload-roles/index.d.ts +10 -17
  260. package/src/workload-roles/index.js +5 -18
  261. package/src/workload-roles/index.js.map +1 -1
@@ -1,5 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.assertGovernedAwsLifecyclePlatform = assertGovernedAwsLifecyclePlatform;
4
+ exports.runGovernedChildProcess = runGovernedChildProcess;
3
5
  exports.createSstChildEnvironment = createSstChildEnvironment;
4
6
  exports.assertSafeSstForwardedArguments = assertSafeSstForwardedArguments;
5
7
  exports.resolveSstDevMode = resolveSstDevMode;
@@ -10,13 +12,18 @@ exports.runSstLifecyclePreflights = runSstLifecyclePreflights;
10
12
  exports.runSstCommand = runSstCommand;
11
13
  const devkit_1 = require("@nx/devkit");
12
14
  const node_child_process_1 = require("node:child_process");
15
+ const node_fs_1 = require("node:fs");
13
16
  const node_path_1 = require("node:path");
17
+ const index_js_1 = require("../../aws-authority/index.js");
18
+ const child_environment_js_1 = require("../../aws/child-environment.js");
19
+ const identity_js_1 = require("../../aws/identity.js");
14
20
  const stages_js_1 = require("../../foundation/stages.js");
15
21
  const personal_stage_js_1 = require("../../local-dev/personal-stage.js");
16
- const index_js_1 = require("../../repository-policy/index.js");
17
- const index_js_2 = require("../../work-markers/index.js");
18
- const logging_preflight_js_1 = require("./logging-preflight.js");
22
+ const federated_credential_transport_js_1 = require("../local-deploy/federated-credential-transport.js");
23
+ const index_js_2 = require("../../repository-policy/index.js");
24
+ const index_js_3 = require("../../work-markers/index.js");
19
25
  const secret_preflight_js_1 = require("./secret-preflight.js");
26
+ const generated_secret_retirement_js_1 = require("./generated-secret-retirement.js");
20
27
  const sst_environment_source_js_1 = require("./sst-environment-source.js");
21
28
  const GOVERNED_STAGES = new Set(['development', 'staging', 'production']);
22
29
  const DEPLOYMENT_STAGES = new Set(['development', 'staging', 'production']);
@@ -30,6 +37,274 @@ const RESERVED_FORWARDED_OPTIONS = new Set([
30
37
  const SST_DEV_MODES = new Set(['basic', 'mono']);
31
38
  const AWS_REGION_PATTERN = /^[a-z]{2}(?:-gov)?-[a-z]+-\d$/;
32
39
  const AWS_ROLE_ARN_PATTERN = /^arn:(aws|aws-us-gov|aws-cn):iam::\d{12}:role\/[A-Za-z0-9+=,.@_/-]{1,512}$/;
40
+ const MAX_CALLER_IDENTITY_BYTES = 1024 * 1024;
41
+ const SST_SIGNAL_GRACE_PERIOD_MS = 10_000;
42
+ const SST_SIGNAL_KILL_WAIT_MS = 5_000;
43
+ const SST_PROCESS_GROUP_POLL_MS = 25;
44
+ const GOVERNED_PROCESS_GROUP_ENV = 'EBK_GOVERNED_PROCESS_GROUP';
45
+ function assertGovernedAwsLifecyclePlatform(platform = process.platform) {
46
+ if (platform === 'win32') {
47
+ throw new Error('Governed AWS infrastructure lifecycle is unavailable on native Windows; use macOS, Linux, or WSL2. Native Windows remains supported for local development and verification.');
48
+ }
49
+ }
50
+ function isMissingProcessError(error) {
51
+ return (typeof error === 'object' &&
52
+ error !== null &&
53
+ 'code' in error &&
54
+ error.code === 'ESRCH');
55
+ }
56
+ function exactWindowsSystemRoot() {
57
+ const values = new Set(Object.entries(process.env)
58
+ .filter(([name, value]) => name.toLowerCase() === 'systemroot' &&
59
+ typeof value === 'string' &&
60
+ value.length > 0)
61
+ .map(([, value]) => value));
62
+ if (values.size !== 1) {
63
+ throw new Error('Workspace SST tree cleanup requires exact SystemRoot.');
64
+ }
65
+ const systemRoot = values.values().next().value;
66
+ if (typeof systemRoot !== 'string' ||
67
+ !(0, node_path_1.isAbsolute)(systemRoot) ||
68
+ /[\0\r\n"]/u.test(systemRoot)) {
69
+ throw new Error('Workspace SST tree cleanup has an unsafe SystemRoot.');
70
+ }
71
+ return systemRoot;
72
+ }
73
+ function exactWindowsTaskkill() {
74
+ const systemRoot = exactWindowsSystemRoot();
75
+ const requested = (0, node_path_1.join)(systemRoot, 'System32', 'taskkill.exe');
76
+ const metadata = (0, node_fs_1.lstatSync)(requested);
77
+ const canonical = (0, node_fs_1.realpathSync)(requested);
78
+ if (metadata.isSymbolicLink() ||
79
+ !metadata.isFile() ||
80
+ canonical.toLowerCase() !== requested.toLowerCase()) {
81
+ throw new Error('Workspace SST taskkill authority is not exact.');
82
+ }
83
+ return { executable: canonical, systemRoot };
84
+ }
85
+ function signalExactGovernedProcessTree(child, signal) {
86
+ const pid = child.pid;
87
+ if (typeof pid !== 'number' || !Number.isSafeInteger(pid) || pid <= 0) {
88
+ throw new Error('Workspace SST child has no exact process identity.');
89
+ }
90
+ if (process.platform === 'win32') {
91
+ const taskkill = exactWindowsTaskkill();
92
+ const terminated = (0, node_child_process_1.spawnSync)(taskkill.executable, ['/pid', String(pid), '/t', '/f'], {
93
+ env: { SystemRoot: taskkill.systemRoot },
94
+ shell: false,
95
+ stdio: 'ignore',
96
+ timeout: 15_000,
97
+ windowsHide: true,
98
+ });
99
+ if ((terminated.error || terminated.signal || terminated.status !== 0) &&
100
+ child.exitCode === null &&
101
+ child.signalCode === null) {
102
+ throw new Error('Workspace SST process-tree termination failed.');
103
+ }
104
+ return;
105
+ }
106
+ try {
107
+ process.kill(-pid, signal);
108
+ }
109
+ catch (error) {
110
+ if (!isMissingProcessError(error))
111
+ throw error;
112
+ if (child.exitCode === null && child.signalCode === null) {
113
+ child.kill(signal);
114
+ }
115
+ }
116
+ }
117
+ function exactGovernedProcessTreeExists(pid) {
118
+ if (process.platform === 'win32')
119
+ return false;
120
+ try {
121
+ process.kill(-pid, 0);
122
+ return true;
123
+ }
124
+ catch (error) {
125
+ if (isMissingProcessError(error))
126
+ return false;
127
+ throw error;
128
+ }
129
+ }
130
+ async function waitForExactGovernedProcessTreeExit(pid, deadline = Date.now() + SST_SIGNAL_GRACE_PERIOD_MS + SST_SIGNAL_KILL_WAIT_MS) {
131
+ while (exactGovernedProcessTreeExists(pid)) {
132
+ if (Date.now() >= deadline) {
133
+ throw new Error('Workspace SST process tree did not stop within the bounded deadline.');
134
+ }
135
+ await new Promise((resolveWait) => setTimeout(resolveWait, SST_PROCESS_GROUP_POLL_MS));
136
+ }
137
+ }
138
+ async function waitForGovernedChildSettlement(childSettled, deadline) {
139
+ const remaining = deadline - Date.now();
140
+ if (remaining <= 0) {
141
+ throw new Error('Workspace SST child did not stop within the bounded deadline.');
142
+ }
143
+ let timeout;
144
+ try {
145
+ await Promise.race([
146
+ childSettled,
147
+ new Promise((_resolve, reject) => {
148
+ timeout = setTimeout(() => reject(new Error('Workspace SST child did not stop within the bounded deadline.')), remaining);
149
+ }),
150
+ ]);
151
+ }
152
+ finally {
153
+ if (timeout)
154
+ clearTimeout(timeout);
155
+ }
156
+ }
157
+ async function terminateRetainedGovernedProcessGroup(pid) {
158
+ if (process.platform === 'win32' || !exactGovernedProcessTreeExists(pid)) {
159
+ return false;
160
+ }
161
+ const deadline = Date.now() + SST_SIGNAL_GRACE_PERIOD_MS + SST_SIGNAL_KILL_WAIT_MS;
162
+ try {
163
+ process.kill(-pid, 'SIGTERM');
164
+ }
165
+ catch (error) {
166
+ if (!isMissingProcessError(error))
167
+ throw error;
168
+ return false;
169
+ }
170
+ const escalation = setTimeout(() => {
171
+ try {
172
+ process.kill(-pid, 'SIGKILL');
173
+ }
174
+ catch (error) {
175
+ if (!isMissingProcessError(error)) {
176
+ process.stderr.write('Governed descendant process-tree escalation failed.\n');
177
+ }
178
+ }
179
+ }, SST_SIGNAL_GRACE_PERIOD_MS);
180
+ try {
181
+ await waitForExactGovernedProcessTreeExit(pid, deadline);
182
+ }
183
+ finally {
184
+ clearTimeout(escalation);
185
+ }
186
+ return true;
187
+ }
188
+ async function stopExactGovernedProcessTree(child, signal, childSettled, isSettled, ownsProcessGroup = true) {
189
+ const pid = child.pid;
190
+ const deadline = Date.now() + SST_SIGNAL_GRACE_PERIOD_MS + SST_SIGNAL_KILL_WAIT_MS;
191
+ if (process.platform === 'win32' || ownsProcessGroup) {
192
+ signalExactGovernedProcessTree(child, signal);
193
+ }
194
+ else {
195
+ child.kill(signal);
196
+ }
197
+ let escalationError;
198
+ const escalation = setTimeout(() => {
199
+ try {
200
+ if (!isSettled() ||
201
+ (ownsProcessGroup && pid && exactGovernedProcessTreeExists(pid))) {
202
+ if (process.platform === 'win32' || ownsProcessGroup) {
203
+ signalExactGovernedProcessTree(child, 'SIGKILL');
204
+ }
205
+ else {
206
+ child.kill('SIGKILL');
207
+ }
208
+ }
209
+ }
210
+ catch (error) {
211
+ escalationError = error;
212
+ }
213
+ }, SST_SIGNAL_GRACE_PERIOD_MS);
214
+ try {
215
+ await waitForGovernedChildSettlement(childSettled, deadline);
216
+ if (ownsProcessGroup && pid && process.platform !== 'win32') {
217
+ await waitForExactGovernedProcessTreeExit(pid, deadline);
218
+ }
219
+ if (escalationError)
220
+ throw escalationError;
221
+ }
222
+ finally {
223
+ clearTimeout(escalation);
224
+ }
225
+ }
226
+ /** Run one exact shell-free child as an owned process tree. The returned
227
+ * promise settles only after child teardown and the bounded caller-supplied
228
+ * close reconciliation complete. */
229
+ function runGovernedChildProcess(input) {
230
+ return new Promise((resolveChildRun) => {
231
+ const environment = { ...input.environment };
232
+ for (const name of Object.keys(environment)) {
233
+ if (name.toUpperCase() === GOVERNED_PROCESS_GROUP_ENV) {
234
+ delete environment[name];
235
+ }
236
+ }
237
+ if (process.platform !== 'win32') {
238
+ environment[GOVERNED_PROCESS_GROUP_ENV] = '1';
239
+ }
240
+ const child = (input.spawnChild ?? node_child_process_1.spawn)(input.executable, [...input.args], {
241
+ cwd: input.cwd,
242
+ detached: process.platform !== 'win32',
243
+ env: environment,
244
+ shell: false,
245
+ stdio: 'inherit',
246
+ });
247
+ let finishing = false;
248
+ let settled = false;
249
+ let spawnError;
250
+ let unregisterSignalCleanup = () => undefined;
251
+ let resolveChildSettled = () => undefined;
252
+ const childSettled = new Promise((resolveChild) => {
253
+ resolveChildSettled = resolveChild;
254
+ });
255
+ const finish = async (status, signal) => {
256
+ if (finishing)
257
+ return;
258
+ finishing = true;
259
+ const result = {
260
+ ...(spawnError ? { error: spawnError } : {}),
261
+ signal,
262
+ status,
263
+ };
264
+ if (!(0, federated_credential_transport_js_1.governedSignalCleanupInProgress)() && child.pid) {
265
+ try {
266
+ if (await terminateRetainedGovernedProcessGroup(child.pid)) {
267
+ result.error ??= new Error('Governed child exited before its exact descendant process tree stopped.');
268
+ }
269
+ }
270
+ catch (error) {
271
+ result.error =
272
+ error instanceof Error
273
+ ? error
274
+ : new Error('Governed child descendant process-tree reconciliation failed.');
275
+ }
276
+ }
277
+ try {
278
+ await input.afterClose?.(result);
279
+ }
280
+ catch (error) {
281
+ result.error =
282
+ error instanceof Error
283
+ ? error
284
+ : new Error('Governed child close reconciliation failed.');
285
+ }
286
+ settled = true;
287
+ unregisterSignalCleanup();
288
+ resolveChildSettled();
289
+ resolveChildRun(result);
290
+ };
291
+ unregisterSignalCleanup = (0, federated_credential_transport_js_1.registerGovernedSignalCleanup)((signal) => stopExactGovernedProcessTree(child, signal, childSettled, () => settled), 100);
292
+ child.once('error', (error) => {
293
+ spawnError = error;
294
+ void finish(null, null);
295
+ });
296
+ child.once('close', (status, signal) => {
297
+ void finish(status, signal);
298
+ });
299
+ });
300
+ }
301
+ function partitionForRegion(region) {
302
+ if (region.startsWith('cn-'))
303
+ return 'aws-cn';
304
+ if (region.startsWith('us-gov-'))
305
+ return 'aws-us-gov';
306
+ return 'aws';
307
+ }
33
308
  function captureSstEnvironmentSource(context) {
34
309
  const projectName = context.projectName;
35
310
  const configured = projectName
@@ -45,24 +320,62 @@ function captureSstEnvironmentSource(context) {
45
320
  workspaceRoot: context.root,
46
321
  });
47
322
  }
48
- function requiresInfrastructureBinding(command, stage) {
323
+ function requiresSourceReconciliation(command, stage) {
49
324
  return (GOVERNED_STAGES.has(stage) &&
50
325
  (command === 'deploy' || command === 'refresh' || command === 'remove'));
51
326
  }
52
- async function authorizeInfrastructureMutation(command, options, context, invocation) {
53
- const { revalidateInfrastructureMutationAuthorization } = await Promise.resolve().then(() => require('../infrastructure-plan/infrastructure-plan.js'));
54
- await revalidateInfrastructureMutationAuthorization(command, options, context, invocation);
327
+ function isGovernedLocalCommand(command) {
328
+ return (command === 'deploy' ||
329
+ command === 'refresh' ||
330
+ command === 'remove' ||
331
+ command === 'unlock');
55
332
  }
56
- async function authorizeLocalUnlock(options, invocation) {
57
- const { revalidateLocalUnlockCallerAuthority } = await Promise.resolve().then(() => require('../infrastructure-plan/infrastructure-plan.js'));
58
- revalidateLocalUnlockCallerAuthority({
59
- invocation,
60
- profile: options.profile,
333
+ async function authorizeLocalUnlock(options, invocation, environment = process.env) {
334
+ const expectedAccount = invocation.environment.AWS_EXPECTED_ACCOUNT_ID?.trim() ?? '';
335
+ const expectedRoleArn = invocation.environment.EBK_EXPECTED_DEPLOYMENT_ROLE_ARN?.trim() ?? '';
336
+ if (!/^\d{12}$/.test(expectedAccount) || !expectedRoleArn) {
337
+ throw new Error('Local unlock requires one exact committed account and temporary assumed role.');
338
+ }
339
+ const ambientExpectedAccount = environment.AWS_EXPECTED_ACCOUNT_ID?.trim();
340
+ if (ambientExpectedAccount && ambientExpectedAccount !== expectedAccount) {
341
+ throw new Error('Ambient AWS_EXPECTED_ACCOUNT_ID does not match committed deployment topology.');
342
+ }
343
+ const result = (0, node_child_process_1.spawnSync)('aws', [
344
+ 'sts',
345
+ 'get-caller-identity',
346
+ '--endpoint-url',
347
+ (0, identity_js_1.canonicalStsEndpoint)(invocation.environment.AWS_REGION?.trim() ?? ''),
348
+ '--output',
349
+ 'json',
350
+ ...(options.profile ? ['--profile', options.profile] : []),
351
+ ], {
352
+ cwd: invocation.cwd,
353
+ encoding: 'utf8',
354
+ env: createSstChildEnvironment(environment, invocation.environment),
355
+ maxBuffer: MAX_CALLER_IDENTITY_BYTES,
356
+ shell: false,
357
+ windowsHide: true,
61
358
  });
62
- }
63
- async function retainInfrastructureConvergence(command, options, context, invocation, applySucceeded) {
64
- const { recordInfrastructureMutationConvergence } = await Promise.resolve().then(() => require('../infrastructure-plan/infrastructure-plan.js'));
65
- return recordInfrastructureMutationConvergence(command, options, context, invocation, applySucceeded);
359
+ if (result.error || result.status !== 0 || result.signal) {
360
+ throw new Error('Local unlock AWS caller identity could not be verified.');
361
+ }
362
+ let caller;
363
+ try {
364
+ caller = JSON.parse(result.stdout);
365
+ }
366
+ catch {
367
+ throw new Error('Local unlock AWS caller identity is not valid JSON.');
368
+ }
369
+ if (!caller ||
370
+ typeof caller !== 'object' ||
371
+ Array.isArray(caller) ||
372
+ !('Account' in caller) ||
373
+ !('Arn' in caller) ||
374
+ caller.Account !== expectedAccount ||
375
+ typeof caller.Arn !== 'string' ||
376
+ !(0, identity_js_1.matchesExpectedAssumedRoleArn)(expectedAccount, caller.Arn, expectedRoleArn)) {
377
+ throw new Error('Local unlock AWS caller does not match the committed account and exact temporary assumed role.');
378
+ }
66
379
  }
67
380
  function projectKind(context) {
68
381
  if (!context.projectName) {
@@ -77,18 +390,21 @@ function projectKind(context) {
77
390
  return foundation ? 'foundation' : 'slice';
78
391
  }
79
392
  function createSstChildEnvironment(ambient, invocation) {
80
- const sanitized = (0, personal_stage_js_1.isFrameworkPersonalStage)(invocation.EBK_CONSUMER_STAGE)
393
+ const stageSanitized = (0, personal_stage_js_1.isFrameworkPersonalStage)(invocation.EBK_CONSUMER_STAGE)
81
394
  ? (0, personal_stage_js_1.createPersonalDevelopmentEnvironment)(invocation.EBK_CONSUMER_STAGE, ambient)
82
395
  : (0, personal_stage_js_1.stripAmbientStageAuthority)(ambient);
396
+ const sanitized = (0, child_environment_js_1.sanitizeGovernedAwsChildEnvironment)(stageSanitized, {
397
+ preserveVerifiedGithubCredentials: stageSanitized.GITHUB_ACTIONS === 'true',
398
+ });
83
399
  delete sanitized.EBK_PRODUCTION_FOUNDATION_PROFILE;
84
400
  delete sanitized.EBK_LOCAL_MUTATION_CAPABILITY_NONCE;
85
401
  delete sanitized.EBK_LOCAL_MUTATION_CAPABILITY_PATH;
402
+ delete sanitized.EBK_GITHUB_JOB_WORKFLOW_REF;
403
+ delete sanitized.EBK_NX_STAGE_LIFECYCLE;
86
404
  // Nx injects task identity, target, hash, terminal, cache, and invocation
87
- // metadata into every executor process. A reviewed infrastructure-plan and
88
- // its mutation-time revalidation intentionally run under different Nx
89
- // targets, so those values are orchestration context rather than desired
90
- // infrastructure inputs. Never let them reach SST, where build tools can
91
- // persist ambient environment values into deployment archives.
405
+ // metadata into every executor process. Those values describe orchestration,
406
+ // not desired infrastructure. Never let them reach SST, where build tools
407
+ // can persist ambient environment values into deployment archives.
92
408
  for (const name of Object.keys(sanitized)) {
93
409
  if (name.startsWith('NX_') || name === 'LERNA_PACKAGE_NAME') {
94
410
  delete sanitized[name];
@@ -124,19 +440,33 @@ function assertSstLifecyclePolicy(command, stage, environment = process.env, dep
124
440
  if (command === 'dev') {
125
441
  throw new Error(`SST dev is not permitted for protected stage ${stage}.`);
126
442
  }
127
- if (!(0, index_js_1.requiresGithubDeployment)(deploymentProfile, stage) &&
443
+ if (command === 'remove' &&
444
+ stage === 'production' &&
445
+ environment.EBK_ALLOW_PRODUCTION_REMOVE !== 'true') {
446
+ throw new Error('Production removal requires EBK_ALLOW_PRODUCTION_REMOVE=true in the governed production environment.');
447
+ }
448
+ if (!(0, index_js_2.requiresGithubDeployment)(deploymentProfile, stage) &&
128
449
  environment.GITHUB_ACTIONS !== 'true') {
129
450
  return;
130
451
  }
131
452
  const repository = environment.GITHUB_REPOSITORY;
132
- const expectedBranch = stage === 'staging' ? 'develop' : 'main';
453
+ const expectedBranch = stage === 'production' ? 'main' : 'develop';
133
454
  const expectedRef = `refs/heads/${expectedBranch}`;
134
- const expectedWorkflowRef = repository
455
+ const expectedCalledWorkflowRef = repository
135
456
  ? `${repository}/.github/workflows/deploy.yml@${expectedRef}`
136
457
  : undefined;
137
458
  const eventName = environment.GITHUB_EVENT_NAME;
138
459
  const allowedEvent = eventName === 'workflow_dispatch' ||
139
- (eventName === 'push' && command === 'deploy');
460
+ (eventName === 'push' &&
461
+ command === 'deploy' &&
462
+ deploymentProfile !== 'starter');
463
+ const expectedCallerWorkflowRef = eventName === 'push'
464
+ ? repository
465
+ ? `${repository}/.github/workflows/ci.yml@${expectedRef}`
466
+ : undefined
467
+ : eventName === 'workflow_dispatch'
468
+ ? expectedCalledWorkflowRef
469
+ : undefined;
140
470
  // These GitHub-provided values are a fail-closed provenance consistency
141
471
  // check, not a credential boundary: a local process can spoof them. Actual
142
472
  // protected-stage authority comes from environment-scoped OIDC role access
@@ -146,14 +476,10 @@ function assertSstLifecyclePolicy(command, stage, environment = process.env, dep
146
476
  environment.GITHUB_ACTIONS === 'true' &&
147
477
  allowedEvent &&
148
478
  environment.GITHUB_REF === expectedRef &&
149
- environment.GITHUB_WORKFLOW_REF === expectedWorkflowRef;
479
+ environment.GITHUB_WORKFLOW_REF === expectedCallerWorkflowRef &&
480
+ environment.EBK_GITHUB_JOB_WORKFLOW_REF === expectedCalledWorkflowRef;
150
481
  if (!workflowProvenanceMatches) {
151
- throw new Error(`SST ${command} for protected stage ${stage} must run from the ${expectedBranch}-branch deploy workflow; pushes may run only deploy and other lifecycle actions require workflow_dispatch.`);
152
- }
153
- if (command === 'remove' &&
154
- stage === 'production' &&
155
- environment.EBK_ALLOW_PRODUCTION_REMOVE !== 'true') {
156
- throw new Error('Production removal requires EBK_ALLOW_PRODUCTION_REMOVE=true in the protected production environment.');
482
+ throw new Error(`SST ${command} for protected stage ${stage} must run from the exact ${expectedBranch}-branch caller and reusable deploy workflows; isolated-profile pushes may run only deploy and other lifecycle actions require workflow_dispatch.`);
157
483
  }
158
484
  }
159
485
  function resolveSstLifecycleStages(options, environment = process.env) {
@@ -222,19 +548,23 @@ function buildSstCommandInvocation(command, options, context, environment = proc
222
548
  stages.mapped) {
223
549
  throw new Error(`Full deploy is not permitted for mapped personal stage ${stages.consumerStage}. Use ebk dev against the ${stages.foundationStage} Foundation.`);
224
550
  }
225
- const operatingPolicy = DEPLOYMENT_STAGES.has(stages.consumerStage) &&
226
- (command === 'deploy' ||
227
- command === 'refresh' ||
228
- command === 'remove' ||
229
- command === 'unlock')
230
- ? (0, index_js_1.readRepositoryOperatingPolicy)(context.root)
551
+ const operatingPolicy = command === 'dev' ||
552
+ (DEPLOYMENT_STAGES.has(stages.consumerStage) &&
553
+ (command === 'deploy' ||
554
+ command === 'refresh' ||
555
+ command === 'remove' ||
556
+ command === 'unlock'))
557
+ ? (0, index_js_2.readRepositoryOperatingPolicy)(context.root)
231
558
  : undefined;
232
559
  assertSstLifecyclePolicy(command, stages.consumerStage, environment, operatingPolicy?.deploymentProfile);
233
- const deploymentAccounts = operatingPolicy
234
- ? (0, index_js_1.configuredDeploymentAccountTopology)(operatingPolicy.deploymentProfile, operatingPolicy.deploymentAccounts)
560
+ const authorityStage = command === 'dev'
561
+ ? 'development'
562
+ : stages.consumerStage;
563
+ const deploymentAccount = operatingPolicy
564
+ ? (0, index_js_2.configuredDeploymentAccountForStage)(operatingPolicy.deploymentProfile, operatingPolicy.deploymentAccounts, authorityStage)
235
565
  : undefined;
236
566
  const deploymentRegion = operatingPolicy
237
- ? (0, index_js_1.configuredDeploymentRegion)(operatingPolicy.deploymentRegion)
567
+ ? (0, index_js_2.configuredDeploymentRegion)(operatingPolicy.deploymentRegion)
238
568
  : undefined;
239
569
  if (deploymentRegion &&
240
570
  options.region?.trim() &&
@@ -245,6 +575,22 @@ function buildSstCommandInvocation(command, options, context, environment = proc
245
575
  !AWS_ROLE_ARN_PATTERN.test(options.expectedRoleArn.trim())) {
246
576
  throw new Error('SST lifecycle expectedRoleArn must be one exact IAM role ARN.');
247
577
  }
578
+ const committedRoleArn = operatingPolicy &&
579
+ deploymentAccount &&
580
+ deploymentRegion &&
581
+ isGovernedLocalCommand(command)
582
+ ? (0, index_js_1.createStageAuthorityIdentity)({
583
+ accountId: deploymentAccount,
584
+ partition: partitionForRegion(deploymentRegion),
585
+ region: deploymentRegion,
586
+ stage: authorityStage,
587
+ }).deploymentRoleArn
588
+ : undefined;
589
+ if (committedRoleArn &&
590
+ options.expectedRoleArn?.trim() &&
591
+ options.expectedRoleArn.trim() !== committedRoleArn) {
592
+ throw new Error(`SST ${command} requires the exact committed ${authorityStage} stage authority role ${committedRoleArn}.`);
593
+ }
248
594
  const profile = options.profile?.trim();
249
595
  if (options.profile !== undefined &&
250
596
  (!profile || profile !== options.profile || profile.length > 128)) {
@@ -252,21 +598,24 @@ function buildSstCommandInvocation(command, options, context, environment = proc
252
598
  }
253
599
  if (operatingPolicy &&
254
600
  environment.GITHUB_ACTIONS !== 'true' &&
255
- (command === 'deploy' ||
256
- command === 'refresh' ||
257
- command === 'remove' ||
258
- command === 'unlock') &&
601
+ command === 'deploy' &&
602
+ environment.EBK_NX_STAGE_LIFECYCLE !== 'local') {
603
+ throw new Error(`Local deploy for ${stages.consumerStage} must be invoked by the governed root stage lifecycle (EBK_NX_STAGE_LIFECYCLE=local).`);
604
+ }
605
+ if (operatingPolicy &&
606
+ environment.GITHUB_ACTIONS !== 'true' &&
607
+ isGovernedLocalCommand(command) &&
259
608
  !options.expectedRoleArn?.trim()) {
260
609
  throw new Error(`Local ${command} for ${stages.consumerStage} requires --expectedRoleArn for one temporary assumed-role session.`);
261
610
  }
262
611
  if (GOVERNED_STAGES.has(stages.consumerStage) &&
263
612
  (command === 'deploy' || command === 'refresh')) {
264
- (0, index_js_2.assertWorkMarkerReport)((0, index_js_2.scanWorkMarkers)(context.root), {
265
- protectedPromotion: true,
613
+ (0, index_js_3.assertWorkMarkerReport)((0, index_js_3.scanWorkMarkers)(context.root), {
614
+ protectedPromotion: stages.consumerStage !== 'development',
266
615
  });
267
616
  }
268
617
  const policyDecision = stages.consumerStage === 'production'
269
- ? (0, index_js_1.assertRepositoryPolicyRequest)(context.root, {
618
+ ? (0, index_js_2.assertRepositoryPolicyRequest)(context.root, {
270
619
  action: command,
271
620
  projects: [
272
621
  {
@@ -278,6 +627,8 @@ function buildSstCommandInvocation(command, options, context, environment = proc
278
627
  })
279
628
  : undefined;
280
629
  const args = [command, '--stage', stages.consumerStage];
630
+ if (command === 'refresh')
631
+ args.push('--yes');
281
632
  if (options.target) {
282
633
  args.push('--target', options.target);
283
634
  }
@@ -297,9 +648,9 @@ function buildSstCommandInvocation(command, options, context, environment = proc
297
648
  ],
298
649
  cwd,
299
650
  environment: {
300
- ...(deploymentAccounts
651
+ ...(deploymentAccount
301
652
  ? {
302
- AWS_EXPECTED_ACCOUNT_ID: deploymentAccounts[stages.consumerStage],
653
+ AWS_EXPECTED_ACCOUNT_ID: deploymentAccount,
303
654
  }
304
655
  : {}),
305
656
  ...(profile ? { AWS_PROFILE: profile } : {}),
@@ -309,9 +660,9 @@ function buildSstCommandInvocation(command, options, context, environment = proc
309
660
  ? { AWS_REGION: options.region.trim() }
310
661
  : {}),
311
662
  EBK_CONSUMER_STAGE: stages.consumerStage,
312
- ...(options.expectedRoleArn?.trim()
663
+ ...((committedRoleArn ?? options.expectedRoleArn?.trim())
313
664
  ? {
314
- EBK_EXPECTED_DEPLOYMENT_ROLE_ARN: options.expectedRoleArn.trim(),
665
+ EBK_EXPECTED_DEPLOYMENT_ROLE_ARN: committedRoleArn ?? options.expectedRoleArn?.trim(),
315
666
  }
316
667
  : {}),
317
668
  EBK_FOUNDATION_STAGE: stages.foundationStage,
@@ -324,35 +675,33 @@ function buildSstCommandInvocation(command, options, context, environment = proc
324
675
  executable: process.execPath,
325
676
  };
326
677
  }
327
- async function runSstLifecyclePreflights(command, options, context, invocation, dependencies = {}) {
328
- if (command === 'deploy' || command === 'dev') {
329
- await (dependencies.logging ?? logging_preflight_js_1.preflightManagedLogGroups)(command, options, context, invocation);
330
- }
678
+ async function runSstLifecyclePreflights(command, options, context, invocation, dependencies = {}, environment = process.env) {
331
679
  if (command === 'deploy') {
332
680
  await (dependencies.secrets ?? secret_preflight_js_1.preflightSliceSecretMetadata)(options, context, invocation);
333
681
  }
334
682
  if (command === 'unlock' &&
335
683
  invocation.environment.EBK_EXPECTED_DEPLOYMENT_ROLE_ARN) {
336
- await (dependencies.unlockAuthorization ?? authorizeLocalUnlock)(options, invocation);
337
- }
338
- if (requiresInfrastructureBinding(command, invocation.environment.EBK_CONSUMER_STAGE)) {
339
- await (dependencies.infrastructureAuthorization ??
340
- authorizeInfrastructureMutation)(command, options, context, invocation);
684
+ await (dependencies.unlockAuthorization ?? authorizeLocalUnlock)(options, invocation, environment);
341
685
  }
342
686
  }
343
687
  async function runSstCommand(command, options, context, dependencies = {}) {
688
+ if (command !== 'dev') {
689
+ assertGovernedAwsLifecyclePlatform(dependencies.platform);
690
+ }
691
+ const environment = dependencies.environment ?? process.env;
692
+ const joinsGovernedProcessGroup = process.platform !== 'win32' &&
693
+ environment[GOVERNED_PROCESS_GROUP_ENV] === '1';
344
694
  let invocation;
345
695
  let environmentSource;
346
696
  try {
347
- invocation = buildSstCommandInvocation(command, options, context);
348
- if (requiresInfrastructureBinding(command, invocation.environment.EBK_CONSUMER_STAGE)) {
349
- // Infrastructure authorization runs an SST plan and SST can rewrite its
350
- // generated environment declaration during that preflight. Capture the
351
- // committed source before any SST-bearing operation so neither the plan
352
- // nor the mutation child can become the restoration baseline.
697
+ invocation = buildSstCommandInvocation(command, options, context, environment);
698
+ if (requiresSourceReconciliation(command, invocation.environment.EBK_CONSUMER_STAGE)) {
699
+ // Native SST can rewrite its generated environment declaration. Capture
700
+ // committed tool-owned source before the child so successful and failed
701
+ // lifecycle commands restore the repository exactly.
353
702
  environmentSource = captureSstEnvironmentSource(context);
354
703
  }
355
- await runSstLifecyclePreflights(command, options, context, invocation, dependencies);
704
+ await runSstLifecyclePreflights(command, options, context, invocation, dependencies, environment);
356
705
  if (environmentSource)
357
706
  (0, sst_environment_source_js_1.reconcileSstEnvironmentSourceTree)(environmentSource);
358
707
  }
@@ -378,22 +727,52 @@ async function runSstCommand(command, options, context, dependencies = {}) {
378
727
  return new Promise((resolve) => {
379
728
  const child = (dependencies.spawnChild ?? node_child_process_1.spawn)(invocation.executable, invocation.args, {
380
729
  cwd: invocation.cwd,
381
- env: createSstChildEnvironment(process.env, invocation.environment),
730
+ detached: process.platform !== 'win32' && !joinsGovernedProcessGroup,
731
+ env: createSstChildEnvironment(environment, invocation.environment),
382
732
  shell: false,
383
733
  stdio: 'inherit',
384
734
  });
385
735
  let settled = false;
736
+ let unregisterSignalCleanup = () => undefined;
737
+ let resolveChildSettled = () => undefined;
738
+ const childSettled = new Promise((resolveChild) => {
739
+ resolveChildSettled = resolveChild;
740
+ });
386
741
  const settle = (success) => {
387
742
  if (settled)
388
743
  return;
389
744
  settled = true;
745
+ unregisterSignalCleanup();
746
+ resolveChildSettled();
390
747
  resolve({ success });
391
748
  };
749
+ unregisterSignalCleanup = (0, federated_credential_transport_js_1.registerGovernedSignalCleanup)((signal) => {
750
+ if (settled)
751
+ return;
752
+ return stopExactGovernedProcessTree(child, signal, childSettled, () => settled, !joinsGovernedProcessGroup);
753
+ }, 100);
392
754
  child.on('error', (error) => {
393
755
  devkit_1.logger.error(`Failed to start workspace SST: ${error.message}`);
394
756
  settle(false);
395
757
  });
396
758
  child.on('close', async (code, signal) => {
759
+ if (settled)
760
+ return;
761
+ let retainedProcessTree = false;
762
+ if (!(0, federated_credential_transport_js_1.governedSignalCleanupInProgress)() &&
763
+ !joinsGovernedProcessGroup &&
764
+ child.pid) {
765
+ try {
766
+ retainedProcessTree = await terminateRetainedGovernedProcessGroup(child.pid);
767
+ }
768
+ catch (error) {
769
+ retainedProcessTree = true;
770
+ devkit_1.logger.error(`Failed to stop retained SST descendant process tree: ${error instanceof Error ? error.message : String(error)}`);
771
+ }
772
+ if (retainedProcessTree) {
773
+ devkit_1.logger.error('SST exited before its exact descendant process tree stopped.');
774
+ }
775
+ }
397
776
  if (code !== 0) {
398
777
  devkit_1.logger.error(signal
399
778
  ? `SST ${command} terminated with signal ${signal}.`
@@ -412,30 +791,21 @@ async function runSstCommand(command, options, context, dependencies = {}) {
412
791
  devkit_1.logger.error(`Failed to restore SST tool-owned source: ${error instanceof Error ? error.message : String(error)}`);
413
792
  }
414
793
  }
415
- const applySucceeded = code === 0 && sourceRestored;
416
- if (requiresInfrastructureBinding(command, invocation.environment.EBK_CONSUMER_STAGE)) {
794
+ const applySucceeded = code === 0 && sourceRestored && !retainedProcessTree;
795
+ let generatedSecretRetirementSucceeded = true;
796
+ if (applySucceeded &&
797
+ command === 'remove' &&
798
+ projectKind(context) === 'slice') {
417
799
  try {
418
- const mutationEvidenceRetained = await (dependencies.infrastructureConvergence ??
419
- retainInfrastructureConvergence)(command, options, context, invocation, applySucceeded);
420
- if (!mutationEvidenceRetained) {
421
- devkit_1.logger.error(`SST ${command} did not retain valid infrastructure mutation evidence.`);
422
- }
423
- // A deploy retains apply-complete-unverified as a successful handoff
424
- // to deployed smoke. It is not terminal convergence; the protected
425
- // finalizer consumes the exact alarm, data, and journey evidence.
426
- // Convergence remains the fail-closed authority for governed
427
- // mutations. A child failure remains failed even if refresh wrote a
428
- // newer state snapshot; that condition is retained as recovery
429
- // evidence and cannot be upgraded into accepted success.
430
- settle(sourceRestored && mutationEvidenceRetained && applySucceeded);
800
+ await (dependencies.generatedSecretRetirement ??
801
+ generated_secret_retirement_js_1.retireGeneratedSecretsAfterSliceRemove)(options, context, invocation, { environment });
431
802
  }
432
803
  catch (error) {
433
- devkit_1.logger.error(`Failed to retain infrastructure convergence: ${error instanceof Error ? error.message : String(error)}`);
434
- settle(false);
804
+ generatedSecretRetirementSucceeded = false;
805
+ devkit_1.logger.error(`Generated stage-secret retirement failed after native Slice removal: ${error instanceof Error ? error.message : String(error)}`);
435
806
  }
436
- return;
437
807
  }
438
- settle(applySucceeded);
808
+ settle(applySucceeded && generatedSecretRetirementSucceeded);
439
809
  });
440
810
  });
441
811
  }