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