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