@empire-builder-kit/nx 1.0.0-rc.4 → 1.0.0-rc.40
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +18 -0
- 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.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 +6 -0
- package/src/cli/deploy-runtime.js +58 -0
- package/src/cli/dev-command.d.ts +16 -4
- package/src/cli/dev-command.js +63 -0
- package/src/cli/main.d.ts +9 -1
- package/src/cli/main.js +139 -20
- package/src/cli/output.d.ts +1 -1
- package/src/cli/output.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 +5 -0
- package/src/developer-agent/operation-registry.js +286 -40
- package/src/developer-agent/planning.js +16 -13
- 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 +162 -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 +40 -6
- package/src/executors/sst-lifecycle/run-sst-command.js +779 -70
- 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/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/tool-versions.d.ts +2 -0
- package/src/generators/preset/tool-versions.js +3 -1
- package/src/generators/preset/workspace-files-adopter-skills.d.ts +7 -0
- package/src/generators/preset/workspace-files-adopter-skills.js +303 -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 +90 -28
- 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-stage-authority.d.ts +6 -0
- package/src/generators/preset/workspace-files-stage-authority.js +518 -0
- package/src/generators/preset/workspace-files-thin-deploy.d.ts +55 -0
- package/src/generators/preset/workspace-files-thin-deploy.js +2473 -0
- package/src/generators/preset/workspace-files-thin-docs.d.ts +7 -0
- package/src/generators/preset/workspace-files-thin-docs.js +352 -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 +1504 -608
- 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 +42 -7
- 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 +92 -329
- package/src/generators/slice/built-in-plan-authorization-infrastructure-files.js +60 -2
- 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 +163 -1196
- 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 +20 -1
- 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 +7 -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 -729
- package/src/generators/slice/built-in-plan-infrastructure-safety-files.js +67 -11
- 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 +157 -28
- 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 +279 -43
- 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-files.d.ts +2 -0
- package/src/generators/slice/built-in-plan-testing-persistence-files.js +178 -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 +80 -29
- 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/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 +39 -8
- package/src/git-policy/github-bootstrap.js +329 -175
- 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 +46 -18
- 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 +14 -2
- 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/registry.d.ts +2 -1
- package/src/secrets/registry.js +18 -1
- package/src/secrets/verification.js +9 -1
- 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,
|
|
@@ -617,9 +992,8 @@ export function product(workloadRoles?: StandardSliceWorkloadRolePreflight) {
|
|
|
617
992
|
userPoolId: foundation.userPool.id,
|
|
618
993
|
});
|
|
619
994
|
const dataExportStore = createStandardDataExportStore({
|
|
620
|
-
bucketName:
|
|
621
|
-
keyAliasName:
|
|
622
|
-
workloadRoleArn: workloadRoleBindings?.api.dataRights.role,
|
|
995
|
+
bucketName: runtimeResourceNames.dataExportBucket,
|
|
996
|
+
keyAliasName: runtimeResourceNames.dataExportKeyAlias,
|
|
623
997
|
});
|
|
624
998
|
|
|
625
999
|
const apiHealthLogs = managedLogGroup(
|
|
@@ -733,9 +1107,35 @@ export function product(workloadRoles?: StandardSliceWorkloadRolePreflight) {
|
|
|
733
1107
|
foundation.discovery.alertTopicArn,
|
|
734
1108
|
);
|
|
735
1109
|
|
|
1110
|
+
const vpcRouteFunctionMaterializer =
|
|
1111
|
+
createVpcRouteFunctionMaterializer({
|
|
1112
|
+
directHandlers: [
|
|
1113
|
+
'api/health.handler',
|
|
1114
|
+
'async/webhook-ingress.handler',
|
|
1115
|
+
],
|
|
1116
|
+
${''}\
|
|
1117
|
+
output: $output,
|
|
1118
|
+
});
|
|
736
1119
|
const api = new sst.aws.ApiGatewayV2('${pascalName}Api', {
|
|
737
|
-
...(performanceDeploymentAuthority ? { vpc: foundation.vpc } : {})
|
|
1120
|
+
${network ? ' ...(performanceDeploymentAuthority ? { vpc: foundation.vpc } : {}),\n' : ''}\
|
|
1121
|
+
transform: {
|
|
1122
|
+
route: {
|
|
1123
|
+
handler: vpcRouteFunctionMaterializer.transform,
|
|
1124
|
+
},
|
|
1125
|
+
},
|
|
738
1126
|
});
|
|
1127
|
+
const routeInVpc = (
|
|
1128
|
+
rawRoute: string,
|
|
1129
|
+
handler: { handler: string } & Record<string, any>,
|
|
1130
|
+
args?: Record<string, any>,
|
|
1131
|
+
) =>
|
|
1132
|
+
vpcRouteFunctionMaterializer.route(
|
|
1133
|
+
rawRoute,
|
|
1134
|
+
handler,
|
|
1135
|
+
args,
|
|
1136
|
+
(registeredRoute, registeredHandler, registeredArgs) =>
|
|
1137
|
+
api.route(registeredRoute, registeredHandler, registeredArgs),
|
|
1138
|
+
);
|
|
739
1139
|
const performanceIngress = performanceDeploymentAuthority
|
|
740
1140
|
? (() => {
|
|
741
1141
|
const originProof = new random.RandomPassword(
|
|
@@ -773,7 +1173,7 @@ export function product(workloadRoles?: StandardSliceWorkloadRolePreflight) {
|
|
|
773
1173
|
}
|
|
774
1174
|
: undefined;
|
|
775
1175
|
api.route(${identifierPrefix}Health.method + ' ' + ${identifierPrefix}Health.path, {
|
|
776
|
-
|
|
1176
|
+
permissions: nativeAuthority.permissions('api-health'),
|
|
777
1177
|
environment: {
|
|
778
1178
|
EBK_SOURCE_COMMIT: releaseSourceCommit,
|
|
779
1179
|
EBK_STAGE: $app.stage,
|
|
@@ -782,13 +1182,14 @@ export function product(workloadRoles?: StandardSliceWorkloadRolePreflight) {
|
|
|
782
1182
|
link: secretLinksFor('api-health'),
|
|
783
1183
|
...lambdaLogging(apiHealthLogs.logGroup.name),
|
|
784
1184
|
});
|
|
785
|
-
|
|
1185
|
+
routeInVpc(
|
|
786
1186
|
${identifierPrefix}MachineAuthorizationProof.method +
|
|
787
1187
|
' ' +
|
|
788
1188
|
${identifierPrefix}MachineAuthorizationProof.path,
|
|
789
1189
|
{
|
|
790
|
-
|
|
1190
|
+
permissions: nativeAuthority.permissions('api-machine-authorization-proof'),
|
|
791
1191
|
environment: {
|
|
1192
|
+
${' ...keyValue.environment,\n'}\
|
|
792
1193
|
AUTH_CLIENT_ID: authClient.id,
|
|
793
1194
|
AUTH_MACHINE_ACCEPTED_CLIENT_IDS:
|
|
794
1195
|
machineAuthorization.acceptedClientIdsJson,
|
|
@@ -798,11 +1199,10 @@ export function product(workloadRoles?: StandardSliceWorkloadRolePreflight) {
|
|
|
798
1199
|
},
|
|
799
1200
|
handler: 'api/machine-authorization.handler',
|
|
800
1201
|
link: [
|
|
801
|
-
|
|
1202
|
+
${'keyValue.link'},
|
|
802
1203
|
...secretLinksFor('api-machine-authorization-proof'),
|
|
803
1204
|
],
|
|
804
1205
|
...lambdaLogging(machineAuthorizationProofTargetLogs.logGroup.name),
|
|
805
|
-
vpc: foundation.vpc,
|
|
806
1206
|
},
|
|
807
1207
|
);
|
|
808
1208
|
for (const route of [
|
|
@@ -819,9 +1219,10 @@ export function product(workloadRoles?: StandardSliceWorkloadRolePreflight) {
|
|
|
819
1219
|
operation: ${identifierPrefix}RevokeRecordDelegation,
|
|
820
1220
|
},
|
|
821
1221
|
]) {
|
|
822
|
-
|
|
823
|
-
|
|
1222
|
+
routeInVpc(route.operation.method + ' ' + route.operation.path, {
|
|
1223
|
+
permissions: nativeAuthority.permissions('api-delegations'),
|
|
824
1224
|
environment: {
|
|
1225
|
+
${' ...keyValue.environment,\n'}\
|
|
825
1226
|
AUTH_CLIENT_ID: authClient.id,
|
|
826
1227
|
AUTH_MACHINE_ACCEPTED_CLIENT_IDS:
|
|
827
1228
|
machineAuthorization.acceptedClientIdsJson,
|
|
@@ -830,9 +1231,8 @@ export function product(workloadRoles?: StandardSliceWorkloadRolePreflight) {
|
|
|
830
1231
|
EBK_STAGE: $app.stage,
|
|
831
1232
|
},
|
|
832
1233
|
handler: route.handler,
|
|
833
|
-
link: [
|
|
1234
|
+
link: [${'keyValue.link'}, ...secretLinksFor('api-delegations')],
|
|
834
1235
|
...lambdaLogging(delegationLogs.logGroup.name),
|
|
835
|
-
vpc: foundation.vpc,
|
|
836
1236
|
});
|
|
837
1237
|
}
|
|
838
1238
|
for (const route of [
|
|
@@ -873,12 +1273,10 @@ export function product(workloadRoles?: StandardSliceWorkloadRolePreflight) {
|
|
|
873
1273
|
operation: ${identifierPrefix}ExpireRecordHold,
|
|
874
1274
|
},
|
|
875
1275
|
]) {
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
...(workloadRoles
|
|
879
|
-
? {}
|
|
880
|
-
: { permissions: dataExportStore.dataRightsPermissions }),
|
|
1276
|
+
routeInVpc(route.operation.method + ' ' + route.operation.path, {
|
|
1277
|
+
permissions: nativeAuthority.permissions('api-data-rights'),
|
|
881
1278
|
environment: {
|
|
1279
|
+
${' ...keyValue.environment,\n'}\
|
|
882
1280
|
AUTH_CLIENT_ID: authClient.id,
|
|
883
1281
|
AUTH_MACHINE_ACCEPTED_CLIENT_IDS:
|
|
884
1282
|
machineAuthorization.acceptedClientIdsJson,
|
|
@@ -891,12 +1289,11 @@ export function product(workloadRoles?: StandardSliceWorkloadRolePreflight) {
|
|
|
891
1289
|
},
|
|
892
1290
|
handler: route.handler,
|
|
893
1291
|
link: [
|
|
894
|
-
|
|
1292
|
+
${'keyValue.link'},
|
|
895
1293
|
foundation.eventBus,
|
|
896
1294
|
...secretLinksFor('api-data-rights'),
|
|
897
1295
|
],
|
|
898
1296
|
...lambdaLogging(dataRightsLogs.logGroup.name),
|
|
899
|
-
vpc: foundation.vpc,
|
|
900
1297
|
});
|
|
901
1298
|
}
|
|
902
1299
|
for (const route of [
|
|
@@ -909,9 +1306,10 @@ export function product(workloadRoles?: StandardSliceWorkloadRolePreflight) {
|
|
|
909
1306
|
operation: ${identifierPrefix}DecideRetentionApproval,
|
|
910
1307
|
},
|
|
911
1308
|
]) {
|
|
912
|
-
|
|
913
|
-
|
|
1309
|
+
routeInVpc(route.operation.method + ' ' + route.operation.path, {
|
|
1310
|
+
permissions: nativeAuthority.permissions('api-database-records'),
|
|
914
1311
|
environment: {
|
|
1312
|
+
${' ...keyValue.environment,\n'}\
|
|
915
1313
|
AUTH_CLIENT_ID: authClient.id,
|
|
916
1314
|
AUTH_MACHINE_ACCEPTED_CLIENT_IDS:
|
|
917
1315
|
machineAuthorization.acceptedClientIdsJson,
|
|
@@ -920,18 +1318,18 @@ export function product(workloadRoles?: StandardSliceWorkloadRolePreflight) {
|
|
|
920
1318
|
EBK_STAGE: $app.stage,
|
|
921
1319
|
},
|
|
922
1320
|
handler: route.handler,
|
|
923
|
-
link: [
|
|
1321
|
+
link: [${'keyValue.link'}, ...secretLinksFor('api-database-records')],
|
|
924
1322
|
...lambdaLogging(dataRetentionReviewLogs.logGroup.name),
|
|
925
|
-
vpc: foundation.vpc,
|
|
926
1323
|
});
|
|
927
1324
|
}
|
|
928
|
-
|
|
1325
|
+
routeInVpc(
|
|
929
1326
|
${identifierPrefix}ProtectedExample.method +
|
|
930
1327
|
' ' +
|
|
931
1328
|
${identifierPrefix}ProtectedExample.path,
|
|
932
1329
|
{
|
|
933
|
-
|
|
1330
|
+
permissions: nativeAuthority.permissions('api-protected'),
|
|
934
1331
|
environment: {
|
|
1332
|
+
${' ...keyValue.environment,\n'}\
|
|
935
1333
|
AUTH_CLIENT_ID: authClient.id,
|
|
936
1334
|
AUTH_MACHINE_ACCEPTED_CLIENT_IDS:
|
|
937
1335
|
machineAuthorization.acceptedClientIdsJson,
|
|
@@ -940,18 +1338,18 @@ export function product(workloadRoles?: StandardSliceWorkloadRolePreflight) {
|
|
|
940
1338
|
EBK_STAGE: $app.stage,
|
|
941
1339
|
},
|
|
942
1340
|
handler: 'api/protected.handler',
|
|
943
|
-
link: [
|
|
1341
|
+
link: [${'keyValue.link'}, ...secretLinksFor('api-protected')],
|
|
944
1342
|
...lambdaLogging(protectedApiLogs.logGroup.name),
|
|
945
|
-
vpc: foundation.vpc,
|
|
946
1343
|
},
|
|
947
1344
|
);
|
|
948
|
-
|
|
1345
|
+
routeInVpc(
|
|
949
1346
|
${identifierPrefix}DatabaseHealth.method +
|
|
950
1347
|
' ' +
|
|
951
1348
|
${identifierPrefix}DatabaseHealth.path,
|
|
952
1349
|
{
|
|
953
|
-
|
|
1350
|
+
permissions: nativeAuthority.permissions('api-database-health'),
|
|
954
1351
|
environment: {
|
|
1352
|
+
${' ...keyValue.environment,\n'}\
|
|
955
1353
|
AUTH_CLIENT_ID: authClient.id,
|
|
956
1354
|
AUTH_MACHINE_ACCEPTED_CLIENT_IDS:
|
|
957
1355
|
machineAuthorization.acceptedClientIdsJson,
|
|
@@ -960,9 +1358,8 @@ export function product(workloadRoles?: StandardSliceWorkloadRolePreflight) {
|
|
|
960
1358
|
EBK_STAGE: $app.stage,
|
|
961
1359
|
},
|
|
962
1360
|
handler: 'database/runtime-health.handler',
|
|
963
|
-
link: [
|
|
1361
|
+
link: [${'keyValue.link'}, ...secretLinksFor('api-database-health')],
|
|
964
1362
|
...lambdaLogging(databaseHealthLogs.logGroup.name),
|
|
965
|
-
vpc: foundation.vpc,
|
|
966
1363
|
},
|
|
967
1364
|
);
|
|
968
1365
|
for (const route of [
|
|
@@ -979,9 +1376,10 @@ export function product(workloadRoles?: StandardSliceWorkloadRolePreflight) {
|
|
|
979
1376
|
operation: ${identifierPrefix}PutRecord,
|
|
980
1377
|
},
|
|
981
1378
|
]) {
|
|
982
|
-
|
|
983
|
-
|
|
1379
|
+
routeInVpc(route.operation.method + ' ' + route.operation.path, {
|
|
1380
|
+
permissions: nativeAuthority.permissions('api-database-records'),
|
|
984
1381
|
environment: {
|
|
1382
|
+
${' ...keyValue.environment,\n'}\
|
|
985
1383
|
AUTH_CLIENT_ID: authClient.id,
|
|
986
1384
|
AUTH_MACHINE_ACCEPTED_CLIENT_IDS:
|
|
987
1385
|
machineAuthorization.acceptedClientIdsJson,
|
|
@@ -992,12 +1390,11 @@ export function product(workloadRoles?: StandardSliceWorkloadRolePreflight) {
|
|
|
992
1390
|
},
|
|
993
1391
|
handler: route.handler,
|
|
994
1392
|
link: [
|
|
995
|
-
|
|
1393
|
+
${'keyValue.link'},
|
|
996
1394
|
foundation.eventBus,
|
|
997
1395
|
...secretLinksFor('api-database-records'),
|
|
998
1396
|
],
|
|
999
1397
|
...lambdaLogging(databaseRecordsLogs.logGroup.name),
|
|
1000
|
-
vpc: foundation.vpc,
|
|
1001
1398
|
});
|
|
1002
1399
|
}
|
|
1003
1400
|
for (const route of [
|
|
@@ -1010,9 +1407,10 @@ export function product(workloadRoles?: StandardSliceWorkloadRolePreflight) {
|
|
|
1010
1407
|
operation: ${identifierPrefix}DecideRecordDeletion,
|
|
1011
1408
|
},
|
|
1012
1409
|
]) {
|
|
1013
|
-
|
|
1014
|
-
|
|
1410
|
+
routeInVpc(route.operation.method + ' ' + route.operation.path, {
|
|
1411
|
+
permissions: nativeAuthority.permissions('api-database-records'),
|
|
1015
1412
|
environment: {
|
|
1413
|
+
${' ...keyValue.environment,\n'}\
|
|
1016
1414
|
AUTH_CLIENT_ID: authClient.id,
|
|
1017
1415
|
AUTH_MACHINE_ACCEPTED_CLIENT_IDS:
|
|
1018
1416
|
machineAuthorization.acceptedClientIdsJson,
|
|
@@ -1021,18 +1419,18 @@ export function product(workloadRoles?: StandardSliceWorkloadRolePreflight) {
|
|
|
1021
1419
|
EBK_STAGE: $app.stage,
|
|
1022
1420
|
},
|
|
1023
1421
|
handler: route.handler,
|
|
1024
|
-
link: [
|
|
1422
|
+
link: [${'keyValue.link'}, ...secretLinksFor('api-database-records')],
|
|
1025
1423
|
...lambdaLogging(databaseRecordsLogs.logGroup.name),
|
|
1026
|
-
vpc: foundation.vpc,
|
|
1027
1424
|
});
|
|
1028
1425
|
}
|
|
1029
|
-
|
|
1426
|
+
routeInVpc(
|
|
1030
1427
|
${identifierPrefix}PublishCreated.method +
|
|
1031
1428
|
' ' +
|
|
1032
1429
|
${identifierPrefix}PublishCreated.path,
|
|
1033
1430
|
{
|
|
1034
|
-
|
|
1431
|
+
permissions: nativeAuthority.permissions('api-publish-created'),
|
|
1035
1432
|
environment: {
|
|
1433
|
+
${' ...keyValue.environment,\n'}\
|
|
1036
1434
|
AUTH_CLIENT_ID: authClient.id,
|
|
1037
1435
|
AUTH_MACHINE_ACCEPTED_CLIENT_IDS:
|
|
1038
1436
|
machineAuthorization.acceptedClientIdsJson,
|
|
@@ -1043,21 +1441,17 @@ export function product(workloadRoles?: StandardSliceWorkloadRolePreflight) {
|
|
|
1043
1441
|
},
|
|
1044
1442
|
handler: 'api/publish-created.handler',
|
|
1045
1443
|
link: [
|
|
1046
|
-
|
|
1444
|
+
${'keyValue.link'},
|
|
1047
1445
|
foundation.eventBus,
|
|
1048
1446
|
...secretLinksFor('api-publish-created'),
|
|
1049
1447
|
],
|
|
1050
1448
|
...lambdaLogging(eventPublisherLogs.logGroup.name),
|
|
1051
|
-
vpc: foundation.vpc,
|
|
1052
1449
|
},
|
|
1053
1450
|
);
|
|
1054
1451
|
const machineAuthorizationProof = new sst.aws.Function(
|
|
1055
1452
|
'${pascalName}MachineAuthorizationProof',
|
|
1056
1453
|
{
|
|
1057
|
-
|
|
1058
|
-
...(workloadRoles
|
|
1059
|
-
? {}
|
|
1060
|
-
: { permissions: machineAuthorization.proof.permissions }),
|
|
1454
|
+
permissions: machineAuthorization.proof.permissions,
|
|
1061
1455
|
environment: {
|
|
1062
1456
|
...machineAuthorization.proof.environment,
|
|
1063
1457
|
AUTH_MACHINE_PROBE_URL:
|
|
@@ -1072,11 +1466,9 @@ export function product(workloadRoles?: StandardSliceWorkloadRolePreflight) {
|
|
|
1072
1466
|
const dataExportCleanup = new sst.aws.Function(
|
|
1073
1467
|
'${pascalName}DataExportCleanup',
|
|
1074
1468
|
{
|
|
1075
|
-
|
|
1076
|
-
...(workloadRoles
|
|
1077
|
-
? {}
|
|
1078
|
-
: { permissions: dataExportStore.cleanupPermissions }),
|
|
1469
|
+
permissions: dataExportStore.cleanupPermissions,
|
|
1079
1470
|
environment: {
|
|
1471
|
+
${' ...keyValue.environment,\n'}\
|
|
1080
1472
|
DATA_EXPORT_BUCKET_NAME: dataExportStore.bucket.bucket,
|
|
1081
1473
|
DATA_EXPORT_KMS_KEY_ARN: dataExportStore.encryptionKey.arn,
|
|
1082
1474
|
EBK_STAGE: $app.stage,
|
|
@@ -1084,24 +1476,21 @@ export function product(workloadRoles?: StandardSliceWorkloadRolePreflight) {
|
|
|
1084
1476
|
},
|
|
1085
1477
|
handler: 'jobs/data-export-cleanup.handler',
|
|
1086
1478
|
link: [
|
|
1087
|
-
|
|
1479
|
+
${'keyValue.link'},
|
|
1088
1480
|
...secretLinksFor('data-export-cleanup'),
|
|
1089
1481
|
],
|
|
1090
1482
|
...lambdaLogging(dataExportCleanupLogs.logGroup.name),
|
|
1091
1483
|
timeout: '5 minutes',
|
|
1092
|
-
|
|
1484
|
+
${''}\
|
|
1093
1485
|
},
|
|
1094
1486
|
);
|
|
1095
1487
|
const dataExportCleanupRule = new aws.cloudwatch.EventRule(
|
|
1096
1488
|
'${pascalName}DataExportCleanupSchedule',
|
|
1097
1489
|
{
|
|
1098
|
-
|
|
1099
|
-
? { name: workloadRoleResourceNames.dataExportCleanupRule }
|
|
1100
|
-
: {}),
|
|
1490
|
+
name: runtimeResourceNames.dataExportCleanupRule,
|
|
1101
1491
|
description:
|
|
1102
1492
|
'Hourly authoritative cleanup of expired confidential export versions.',
|
|
1103
|
-
|
|
1104
|
-
state: 'ENABLED',
|
|
1493
|
+
...backgroundSchedules.exportCleanup,
|
|
1105
1494
|
tags: {
|
|
1106
1495
|
'ebk:management': 'standard-data-export',
|
|
1107
1496
|
'ebk:slice': '${name}',
|
|
@@ -1146,9 +1535,7 @@ export function product(workloadRoles?: StandardSliceWorkloadRolePreflight) {
|
|
|
1146
1535
|
const eventDlq = new sst.aws.Queue('${pascalName}EventDlq', {
|
|
1147
1536
|
transform: {
|
|
1148
1537
|
queue: {
|
|
1149
|
-
|
|
1150
|
-
? { name: workloadRoleResourceNames.eventDlq }
|
|
1151
|
-
: {}),
|
|
1538
|
+
name: runtimeResourceNames.eventDlq,
|
|
1152
1539
|
messageRetentionSeconds: 14 * 24 * 60 * 60,
|
|
1153
1540
|
},
|
|
1154
1541
|
},
|
|
@@ -1157,9 +1544,7 @@ export function product(workloadRoles?: StandardSliceWorkloadRolePreflight) {
|
|
|
1157
1544
|
dlq: { queue: eventDlq.arn, retry: 5 },
|
|
1158
1545
|
transform: {
|
|
1159
1546
|
queue: {
|
|
1160
|
-
|
|
1161
|
-
? { name: workloadRoleResourceNames.eventQueue }
|
|
1162
|
-
: {}),
|
|
1547
|
+
name: runtimeResourceNames.eventQueue,
|
|
1163
1548
|
messageRetentionSeconds: 4 * 24 * 60 * 60,
|
|
1164
1549
|
visibilityTimeoutSeconds: 120,
|
|
1165
1550
|
},
|
|
@@ -1171,9 +1556,7 @@ export function product(workloadRoles?: StandardSliceWorkloadRolePreflight) {
|
|
|
1171
1556
|
attributes: [{ name: 'report_digest', type: 'S' }],
|
|
1172
1557
|
billingMode: 'PAY_PER_REQUEST',
|
|
1173
1558
|
hashKey: 'report_digest',
|
|
1174
|
-
|
|
1175
|
-
? { name: workloadRoleResourceNames.asyncRecoveryLedger }
|
|
1176
|
-
: {}),
|
|
1559
|
+
name: runtimeResourceNames.asyncRecoveryLedger,
|
|
1177
1560
|
pointInTimeRecovery: { enabled: true },
|
|
1178
1561
|
tags: {
|
|
1179
1562
|
'ebk:async-recovery-ledger': 'true',
|
|
@@ -1188,19 +1571,18 @@ export function product(workloadRoles?: StandardSliceWorkloadRolePreflight) {
|
|
|
1188
1571
|
);
|
|
1189
1572
|
eventQueue.subscribe({
|
|
1190
1573
|
environment: {
|
|
1574
|
+
${' ...keyValue.environment,\n'}\
|
|
1191
1575
|
EBK_STAGE: $app.stage,
|
|
1192
1576
|
EBK_WORKLOAD_IDENTITY: '${name}:event-consumer',
|
|
1193
1577
|
},
|
|
1194
1578
|
handler: 'api/event-consumer.handler',
|
|
1195
|
-
link: [
|
|
1579
|
+
link: [${'keyValue.link'}, ...secretLinksFor('event-consumer')],
|
|
1580
|
+
permissions: nativeAuthority.permissions('event-consumer'),
|
|
1196
1581
|
...lambdaLogging(eventConsumerLogs.logGroup.name),
|
|
1197
1582
|
timeout: '1 minute',
|
|
1198
|
-
|
|
1583
|
+
${''}\
|
|
1199
1584
|
}, {
|
|
1200
1585
|
batch: { partialResponses: true },
|
|
1201
|
-
transform: {
|
|
1202
|
-
function: workloadRoleBindings?.queue.eventConsumer,
|
|
1203
|
-
},
|
|
1204
1586
|
});
|
|
1205
1587
|
foundation.eventBus.subscribeQueue(
|
|
1206
1588
|
'${pascalName}CreatedEventRule',
|
|
@@ -1208,7 +1590,7 @@ export function product(workloadRoles?: StandardSliceWorkloadRolePreflight) {
|
|
|
1208
1590
|
{
|
|
1209
1591
|
pattern: {
|
|
1210
1592
|
detail: { meta: { stage: [$app.stage] } },
|
|
1211
|
-
detailType:
|
|
1593
|
+
detailType: sameSliceEventDetailTypes(EventContractSchema),
|
|
1212
1594
|
source: ['${name}'],
|
|
1213
1595
|
},
|
|
1214
1596
|
},
|
|
@@ -1258,9 +1640,7 @@ export function product(workloadRoles?: StandardSliceWorkloadRolePreflight) {
|
|
|
1258
1640
|
{
|
|
1259
1641
|
transform: {
|
|
1260
1642
|
queue: {
|
|
1261
|
-
|
|
1262
|
-
? { name: workloadRoleResourceNames.asyncTargetDlq }
|
|
1263
|
-
: {}),
|
|
1643
|
+
name: runtimeResourceNames.asyncTargetDlq,
|
|
1264
1644
|
messageRetentionSeconds: 14 * 24 * 60 * 60,
|
|
1265
1645
|
tags: {
|
|
1266
1646
|
'ebk:async-recovery-source': 'async-target-dlq',
|
|
@@ -1274,9 +1654,7 @@ export function product(workloadRoles?: StandardSliceWorkloadRolePreflight) {
|
|
|
1274
1654
|
const providerDlq = new sst.aws.Queue('${pascalName}ProviderDlq', {
|
|
1275
1655
|
transform: {
|
|
1276
1656
|
queue: {
|
|
1277
|
-
|
|
1278
|
-
? { name: workloadRoleResourceNames.providerDlq }
|
|
1279
|
-
: {}),
|
|
1657
|
+
name: runtimeResourceNames.providerDlq,
|
|
1280
1658
|
messageRetentionSeconds: 14 * 24 * 60 * 60,
|
|
1281
1659
|
tags: {
|
|
1282
1660
|
'ebk:async-recovery-source': 'provider-dlq',
|
|
@@ -1290,9 +1668,7 @@ export function product(workloadRoles?: StandardSliceWorkloadRolePreflight) {
|
|
|
1290
1668
|
dlq: { queue: providerDlq.arn, retry: 5 },
|
|
1291
1669
|
transform: {
|
|
1292
1670
|
queue: {
|
|
1293
|
-
|
|
1294
|
-
? { name: workloadRoleResourceNames.providerQueue }
|
|
1295
|
-
: {}),
|
|
1671
|
+
name: runtimeResourceNames.providerQueue,
|
|
1296
1672
|
messageRetentionSeconds: 4 * 24 * 60 * 60,
|
|
1297
1673
|
tags: {
|
|
1298
1674
|
'ebk:async-recovery-destination': 'provider-queue',
|
|
@@ -1305,10 +1681,11 @@ export function product(workloadRoles?: StandardSliceWorkloadRolePreflight) {
|
|
|
1305
1681
|
});
|
|
1306
1682
|
const reliability = create${pascalName}Reliability({
|
|
1307
1683
|
alertTopicArn: foundation.discovery.alertTopicArn,
|
|
1308
|
-
alertTopicName:
|
|
1309
|
-
|
|
1684
|
+
alertTopicName: foundation.discovery.alertTopicArn.apply(
|
|
1685
|
+
(alertTopicArn: string) => alertTopicArn.split(':').at(-1) ?? '',
|
|
1686
|
+
),
|
|
1310
1687
|
apiId: api.id,
|
|
1311
|
-
|
|
1688
|
+
${' databaseTableName: keyValue.table.name,\n'}\
|
|
1312
1689
|
functionName: dataExportCleanup.name,
|
|
1313
1690
|
providerQueueName: providerQueue.name,
|
|
1314
1691
|
});
|
|
@@ -1356,6 +1733,9 @@ export function product(workloadRoles?: StandardSliceWorkloadRolePreflight) {
|
|
|
1356
1733
|
);
|
|
1357
1734
|
const runtimeConfiguration = protectedStage && !$dev
|
|
1358
1735
|
? (() => {
|
|
1736
|
+
const monitorRole = nativeAuthority.createAppConfigMonitorRole(
|
|
1737
|
+
'${pascalName}ConfigurationMonitorRole',
|
|
1738
|
+
);
|
|
1359
1739
|
const application = new aws.appconfig.Application(
|
|
1360
1740
|
'${pascalName}ConfigurationApplication',
|
|
1361
1741
|
{
|
|
@@ -1381,8 +1761,7 @@ export function product(workloadRoles?: StandardSliceWorkloadRolePreflight) {
|
|
|
1381
1761
|
reliability.providerDeadline,
|
|
1382
1762
|
].map((alarm) => ({
|
|
1383
1763
|
alarmArn: alarm.arn,
|
|
1384
|
-
alarmRoleArn:
|
|
1385
|
-
workloadRoleBindings!.configuration.monitor.roleArn,
|
|
1764
|
+
alarmRoleArn: monitorRole.arn,
|
|
1386
1765
|
})),
|
|
1387
1766
|
name: $app.stage,
|
|
1388
1767
|
tags: {
|
|
@@ -1523,6 +1902,7 @@ export function product(workloadRoles?: StandardSliceWorkloadRolePreflight) {
|
|
|
1523
1902
|
providerQueue.subscribe(
|
|
1524
1903
|
{
|
|
1525
1904
|
environment: {
|
|
1905
|
+
${' ...keyValue.environment,\n'}\
|
|
1526
1906
|
EBK_STAGE: $app.stage,
|
|
1527
1907
|
EBK_WORKLOAD_IDENTITY: '${name}:async-provider-worker',
|
|
1528
1908
|
EBK_WORKSPACE: '${workspace}',
|
|
@@ -1530,26 +1910,24 @@ export function product(workloadRoles?: StandardSliceWorkloadRolePreflight) {
|
|
|
1530
1910
|
},
|
|
1531
1911
|
handler: 'async/provider-worker.handler',
|
|
1532
1912
|
link: [
|
|
1533
|
-
|
|
1913
|
+
${'keyValue.link'},
|
|
1534
1914
|
providerQueue,
|
|
1535
1915
|
...secretLinksFor('async-provider-worker'),
|
|
1536
1916
|
],
|
|
1917
|
+
permissions: nativeAuthority.permissions('async-provider-worker'),
|
|
1537
1918
|
...lambdaLogging(asyncProviderWorkerLogs.logGroup.name),
|
|
1538
1919
|
timeout: '1 minute',
|
|
1539
|
-
|
|
1920
|
+
${''}\
|
|
1540
1921
|
},
|
|
1541
1922
|
{
|
|
1542
1923
|
batch: { partialResponses: true },
|
|
1543
|
-
transform: {
|
|
1544
|
-
function: workloadRoleBindings?.async.providerWorker,
|
|
1545
|
-
},
|
|
1546
1924
|
},
|
|
1547
1925
|
configurationDeployment
|
|
1548
1926
|
? { dependsOn: [configurationDeployment] }
|
|
1549
1927
|
: {},
|
|
1550
1928
|
);
|
|
1551
1929
|
api.route('POST /${name}/webhooks/provider', {
|
|
1552
|
-
|
|
1930
|
+
permissions: nativeAuthority.permissions('async-webhook-ingress'),
|
|
1553
1931
|
environment: {
|
|
1554
1932
|
EBK_ASYNC_LEDGER_TABLE: asyncRecoveryLedger.name,
|
|
1555
1933
|
EBK_STAGE: $app.stage,
|
|
@@ -1568,8 +1946,10 @@ export function product(workloadRoles?: StandardSliceWorkloadRolePreflight) {
|
|
|
1568
1946
|
const asyncOutboxRelay = new sst.aws.Function(
|
|
1569
1947
|
'${pascalName}AsyncOutboxRelay',
|
|
1570
1948
|
{
|
|
1571
|
-
|
|
1949
|
+
permissions: nativeAuthority.permissions('async-outbox-relay'),
|
|
1572
1950
|
environment: {
|
|
1951
|
+
${' ...keyValue.environment,\n'}\
|
|
1952
|
+
${''}\
|
|
1573
1953
|
EBK_STAGE: $app.stage,
|
|
1574
1954
|
EBK_WORKLOAD_IDENTITY: '${name}:async-outbox-relay',
|
|
1575
1955
|
EBK_WORKSPACE: '${workspace}',
|
|
@@ -1577,38 +1957,117 @@ export function product(workloadRoles?: StandardSliceWorkloadRolePreflight) {
|
|
|
1577
1957
|
},
|
|
1578
1958
|
handler: 'async/outbox-relay.handler',
|
|
1579
1959
|
link: [
|
|
1580
|
-
|
|
1960
|
+
${'keyValue.link'},
|
|
1581
1961
|
foundation.eventBus,
|
|
1582
1962
|
...secretLinksFor('async-outbox-relay'),
|
|
1583
1963
|
],
|
|
1584
1964
|
...lambdaLogging(asyncOutboxRelayLogs.logGroup.name),
|
|
1585
1965
|
timeout: '1 minute',
|
|
1586
|
-
|
|
1966
|
+
${''}\
|
|
1587
1967
|
},
|
|
1588
1968
|
);
|
|
1969
|
+
${sql
|
|
1970
|
+
? ` const sqlOutbox = createSqlOutboxInfrastructure({
|
|
1971
|
+
workspace: '${workspace}',
|
|
1972
|
+
database, eventBus: foundation.eventBus, eventBusName: foundation.discovery.eventBusName,
|
|
1973
|
+
secretLinks: secretLinksFor('async-outbox-relay'),
|
|
1974
|
+
vpc: foundation.vpc, schedule: backgroundSchedules.sqlOutboxRecovery,
|
|
1975
|
+
alertTopicArn: foundation.discovery.alertTopicArn,
|
|
1976
|
+
managedLogGroup, lambdaLogging,
|
|
1977
|
+
${''}\
|
|
1978
|
+
});
|
|
1979
|
+
const sqlOutboxRelayErrorAlarm = new aws.cloudwatch.MetricAlarm(
|
|
1980
|
+
'${pascalName}SqlOutboxRelayErrors',
|
|
1981
|
+
{
|
|
1982
|
+
alarmActions: [foundation.discovery.alertTopicArn],
|
|
1983
|
+
alarmDescription: operationalAlarmDescription({
|
|
1984
|
+
businessSymptom: 'Relational event delivery is failing.',
|
|
1985
|
+
id: 'sql-outbox-relay-errors',
|
|
1986
|
+
runbook: 'docs/runbooks/async-recovery.md',
|
|
1987
|
+
}),
|
|
1988
|
+
comparisonOperator: 'GreaterThanOrEqualToThreshold',
|
|
1989
|
+
dimensions: { FunctionName: sqlOutbox.relay.name },
|
|
1990
|
+
evaluationPeriods: 1,
|
|
1991
|
+
metricName: 'Errors',
|
|
1992
|
+
name: '${name}-' + $app.stage + '-sql-outbox-relay-errors',
|
|
1993
|
+
namespace: 'AWS/Lambda',
|
|
1994
|
+
period: 300,
|
|
1995
|
+
statistic: 'Sum',
|
|
1996
|
+
threshold: 1,
|
|
1997
|
+
treatMissingData: 'notBreaching',
|
|
1998
|
+
},
|
|
1999
|
+
);
|
|
2000
|
+
// The relay counts every unpublished intent; Minimum ignores healthy
|
|
2001
|
+
// in-flight batches and alarms only when no run in an hour drained it.
|
|
2002
|
+
const sqlOutboxBacklogAlarm = new aws.cloudwatch.MetricAlarm(
|
|
2003
|
+
'${pascalName}SqlOutboxBacklog',
|
|
2004
|
+
{
|
|
2005
|
+
alarmActions: [foundation.discovery.alertTopicArn],
|
|
2006
|
+
alarmDescription: operationalAlarmDescription({
|
|
2007
|
+
businessSymptom: 'Relational event intents remain undelivered.',
|
|
2008
|
+
id: 'sql-outbox-backlog',
|
|
2009
|
+
runbook: 'docs/runbooks/async-recovery.md',
|
|
2010
|
+
}),
|
|
2011
|
+
comparisonOperator: 'GreaterThanOrEqualToThreshold',
|
|
2012
|
+
dimensions: { Slice: '${name}', Stage: $app.stage },
|
|
2013
|
+
evaluationPeriods: 2,
|
|
2014
|
+
metricName: 'SqlOutboxBacklog',
|
|
2015
|
+
name: '${name}-' + $app.stage + '-sql-outbox-backlog',
|
|
2016
|
+
namespace: 'EBK/Async',
|
|
2017
|
+
period: 3600,
|
|
2018
|
+
statistic: 'Minimum',
|
|
2019
|
+
threshold: 1,
|
|
2020
|
+
treatMissingData: 'notBreaching',
|
|
2021
|
+
},
|
|
2022
|
+
);
|
|
2023
|
+
const sqlOutboxAttemptsAlarm = new aws.cloudwatch.MetricAlarm(
|
|
2024
|
+
'${pascalName}SqlOutboxAttempts',
|
|
2025
|
+
{
|
|
2026
|
+
alarmActions: [foundation.discovery.alertTopicArn],
|
|
2027
|
+
alarmDescription: operationalAlarmDescription({
|
|
2028
|
+
businessSymptom:
|
|
2029
|
+
'A relational event intent is repeatedly failing delivery.',
|
|
2030
|
+
id: 'sql-outbox-attempts',
|
|
2031
|
+
runbook: 'docs/runbooks/async-recovery.md',
|
|
2032
|
+
}),
|
|
2033
|
+
comparisonOperator: 'GreaterThanOrEqualToThreshold',
|
|
2034
|
+
dimensions: { Slice: '${name}', Stage: $app.stage },
|
|
2035
|
+
evaluationPeriods: 1,
|
|
2036
|
+
metricName: 'SqlOutboxMaxAttempts',
|
|
2037
|
+
name: '${name}-' + $app.stage + '-sql-outbox-attempts',
|
|
2038
|
+
namespace: 'EBK/Async',
|
|
2039
|
+
period: 3600,
|
|
2040
|
+
statistic: 'Maximum',
|
|
2041
|
+
threshold: 10,
|
|
2042
|
+
treatMissingData: 'notBreaching',
|
|
2043
|
+
},
|
|
2044
|
+
);
|
|
2045
|
+
`
|
|
2046
|
+
: ''}\
|
|
1589
2047
|
const asyncInboxCleanup = new sst.aws.Function(
|
|
1590
2048
|
'${pascalName}AsyncInboxCleanup',
|
|
1591
2049
|
{
|
|
1592
|
-
|
|
2050
|
+
permissions: nativeAuthority.permissions('async-inbox-cleanup'),
|
|
1593
2051
|
environment: {
|
|
2052
|
+
${' ...keyValue.environment,\n'}\
|
|
1594
2053
|
EBK_STAGE: $app.stage,
|
|
1595
2054
|
EBK_WORKLOAD_IDENTITY: '${name}:async-inbox-cleanup',
|
|
1596
2055
|
EBK_WORKSPACE: '${workspace}',
|
|
1597
2056
|
},
|
|
1598
2057
|
handler: 'async/inbox-cleanup.handler',
|
|
1599
2058
|
link: [
|
|
1600
|
-
|
|
2059
|
+
${'keyValue.link'},
|
|
1601
2060
|
...secretLinksFor('async-inbox-cleanup'),
|
|
1602
2061
|
],
|
|
1603
2062
|
...lambdaLogging(asyncInboxCleanupLogs.logGroup.name),
|
|
1604
2063
|
timeout: '5 minutes',
|
|
1605
|
-
|
|
2064
|
+
${''}\
|
|
1606
2065
|
},
|
|
1607
2066
|
);
|
|
1608
2067
|
const asyncScheduleTarget = new sst.aws.Function(
|
|
1609
2068
|
'${pascalName}AsyncScheduleTarget',
|
|
1610
2069
|
{
|
|
1611
|
-
|
|
2070
|
+
permissions: nativeAuthority.permissions('async-schedule-target'),
|
|
1612
2071
|
environment: {
|
|
1613
2072
|
EBK_STAGE: $app.stage,
|
|
1614
2073
|
EBK_WORKLOAD_IDENTITY: '${name}:async-schedule-target',
|
|
@@ -1623,7 +2082,7 @@ export function product(workloadRoles?: StandardSliceWorkloadRolePreflight) {
|
|
|
1623
2082
|
const asyncWorkflowTask = new sst.aws.Function(
|
|
1624
2083
|
'${pascalName}AsyncWorkflowTask',
|
|
1625
2084
|
{
|
|
1626
|
-
|
|
2085
|
+
permissions: nativeAuthority.permissions('async-workflow-task'),
|
|
1627
2086
|
environment: {
|
|
1628
2087
|
EBK_ASYNC_LEDGER_TABLE: asyncRecoveryLedger.name,
|
|
1629
2088
|
EBK_STAGE: $app.stage,
|
|
@@ -1634,7 +2093,7 @@ export function product(workloadRoles?: StandardSliceWorkloadRolePreflight) {
|
|
|
1634
2093
|
link: secretLinksFor('async-workflow-task'),
|
|
1635
2094
|
...lambdaLogging(
|
|
1636
2095
|
asyncWorkflowTaskLogs.logGroup.name,
|
|
1637
|
-
|
|
2096
|
+
runtimeResourceNames.workflowTaskFunction,
|
|
1638
2097
|
),
|
|
1639
2098
|
timeout: '15 minutes',
|
|
1640
2099
|
},
|
|
@@ -1655,9 +2114,9 @@ export function product(workloadRoles?: StandardSliceWorkloadRolePreflight) {
|
|
|
1655
2114
|
const outboxRelayRule = new aws.cloudwatch.EventRule(
|
|
1656
2115
|
'${pascalName}OutboxRelaySchedule',
|
|
1657
2116
|
{
|
|
1658
|
-
description:
|
|
1659
|
-
|
|
1660
|
-
|
|
2117
|
+
description:
|
|
2118
|
+
'Durable DynamoDB outbox relay; cadence and pause are independent of SQL capacity.',
|
|
2119
|
+
...backgroundSchedules.outbox,
|
|
1661
2120
|
tags: {
|
|
1662
2121
|
'ebk:slice': '${name}',
|
|
1663
2122
|
'ebk:stage': $app.stage,
|
|
@@ -1669,8 +2128,7 @@ export function product(workloadRoles?: StandardSliceWorkloadRolePreflight) {
|
|
|
1669
2128
|
{
|
|
1670
2129
|
description:
|
|
1671
2130
|
'Bounded cleanup of expired async inbox idempotency records.',
|
|
1672
|
-
|
|
1673
|
-
state: 'ENABLED',
|
|
2131
|
+
...backgroundSchedules.inboxCleanup,
|
|
1674
2132
|
tags: {
|
|
1675
2133
|
'ebk:slice': '${name}',
|
|
1676
2134
|
'ebk:stage': $app.stage,
|
|
@@ -1778,6 +2236,10 @@ export function product(workloadRoles?: StandardSliceWorkloadRolePreflight) {
|
|
|
1778
2236
|
);
|
|
1779
2237
|
let recordExportWorkflow: any;
|
|
1780
2238
|
if (!$dev && asyncAdaptersConfigured) {
|
|
2239
|
+
const recordExportWorkflowRole = nativeAuthority.createStepFunctionsRole(
|
|
2240
|
+
'${pascalName}RecordExportWorkflowRole',
|
|
2241
|
+
'async-workflow-orchestration',
|
|
2242
|
+
);
|
|
1781
2243
|
recordExportWorkflow = new aws.sfn.StateMachine(
|
|
1782
2244
|
'${pascalName}RecordExportWorkflow',
|
|
1783
2245
|
{
|
|
@@ -1921,8 +2383,7 @@ export function product(workloadRoles?: StandardSliceWorkloadRolePreflight) {
|
|
|
1921
2383
|
'ebk-${recordExportWorkflowName}-' +
|
|
1922
2384
|
$app.stage +
|
|
1923
2385
|
'-record-export-v1',
|
|
1924
|
-
roleArn:
|
|
1925
|
-
workloadRoleBindings!.async.workflowOrchestration.roleArn,
|
|
2386
|
+
roleArn: recordExportWorkflowRole.arn,
|
|
1926
2387
|
tags: {
|
|
1927
2388
|
'ebk:slice': '${name}',
|
|
1928
2389
|
'ebk:stage': $app.stage,
|
|
@@ -1933,14 +2394,18 @@ export function product(workloadRoles?: StandardSliceWorkloadRolePreflight) {
|
|
|
1933
2394
|
);
|
|
1934
2395
|
}
|
|
1935
2396
|
|
|
2397
|
+
const webServerLogging = lambdaLogging(nextServerLogs.logGroup.name);
|
|
1936
2398
|
const web = new sst.aws.Nextjs('${pascalName}Web', {
|
|
2399
|
+
buildCommand: 'node ../tools/ebk-next-build-environment.mjs',
|
|
2400
|
+
invalidation: { wait: true },
|
|
1937
2401
|
dev: {
|
|
1938
|
-
command:
|
|
2402
|
+
command: 'node tools/ebk-next-dev.mjs',
|
|
1939
2403
|
directory: '.',
|
|
1940
2404
|
title: '${pascalName} web',
|
|
1941
2405
|
url: \`http://127.0.0.1:\${devPort}/${name}\`,
|
|
1942
2406
|
},
|
|
1943
2407
|
environment: {
|
|
2408
|
+
${' ...keyValue.environment,\n'}\
|
|
1944
2409
|
AUTH_CLIENT_ID: authClient.id,
|
|
1945
2410
|
AUTH_USER_POOL_ID: foundation.userPool.id,
|
|
1946
2411
|
EBK_SOURCE_COMMIT: releaseSourceCommit,
|
|
@@ -1967,46 +2432,69 @@ export function product(workloadRoles?: StandardSliceWorkloadRolePreflight) {
|
|
|
1967
2432
|
...runtimeConfigurationEnvironment,
|
|
1968
2433
|
NEXT_PUBLIC_API_URL: api.url,
|
|
1969
2434
|
},
|
|
1970
|
-
link: [database, foundation.userPool],
|
|
2435
|
+
link: [keyValue.link, ${sql ? 'database, foundation.eventBus, ' : ''}foundation.userPool],
|
|
1971
2436
|
path: 'web',
|
|
1972
2437
|
router: { instance: foundation.router, path: '/${name}' },
|
|
1973
2438
|
transform: {
|
|
1974
|
-
assets:
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
}
|
|
1982
|
-
: undefined,
|
|
2439
|
+
assets: {
|
|
2440
|
+
transform: {
|
|
2441
|
+
bucket: {
|
|
2442
|
+
bucket: runtimeResourceNames.webAssetsBucket,
|
|
2443
|
+
},
|
|
2444
|
+
},
|
|
2445
|
+
},
|
|
1983
2446
|
imageOptimizer: {
|
|
1984
|
-
|
|
2447
|
+
permissions: nativeAuthority.permissions('web-image-optimizer'),
|
|
2448
|
+
hook: {
|
|
2449
|
+
async postbuild(directory: string) {
|
|
2450
|
+
removeFinderMetadataFromBuiltFunction(directory);
|
|
2451
|
+
},
|
|
2452
|
+
},
|
|
1985
2453
|
link: secretLinksFor('web-image-optimizer'),
|
|
1986
2454
|
...lambdaLogging(nextImageOptimizerLogs.logGroup.name),
|
|
1987
2455
|
},
|
|
1988
2456
|
revalidationEventsSubscriber: {
|
|
1989
|
-
|
|
2457
|
+
permissions: nativeAuthority.permissions('web-revalidation-subscriber'),
|
|
1990
2458
|
link: secretLinksFor('web-revalidation-subscriber'),
|
|
1991
2459
|
...lambdaLogging(nextRevalidationSubscriberLogs.logGroup.name),
|
|
1992
2460
|
},
|
|
1993
2461
|
revalidationSeeder: {
|
|
1994
|
-
|
|
2462
|
+
permissions: nativeAuthority.permissions('web-revalidation-seeder'),
|
|
1995
2463
|
link: secretLinksFor('web-revalidation-seeder'),
|
|
1996
2464
|
...lambdaLogging(nextRevalidationSeederLogs.logGroup.name),
|
|
1997
2465
|
},
|
|
1998
|
-
server: {
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
'web-server',
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
)
|
|
2006
|
-
|
|
2466
|
+
server(args: Record<string, unknown>) {
|
|
2467
|
+
args.permissions = $resolve({
|
|
2468
|
+
inherited: args.permissions,
|
|
2469
|
+
additional: nativeAuthority.permissions('web-server'),
|
|
2470
|
+
}).apply(({ inherited, additional }) => [
|
|
2471
|
+
...((inherited ?? []) as unknown[]),
|
|
2472
|
+
...additional,
|
|
2473
|
+
]);
|
|
2474
|
+
args.hook = {
|
|
2475
|
+
async postbuild(directory: string) {
|
|
2476
|
+
removeFinderMetadataFromBuiltFunction(directory);
|
|
2477
|
+
},
|
|
2478
|
+
};
|
|
2479
|
+
// SST applies this transform to both its dev runtime and deployed
|
|
2480
|
+
// server. Preserve native/site links; secret links are additive.
|
|
2481
|
+
args.link = $output(args.link ?? []).apply((links: unknown[]) => [
|
|
2482
|
+
...links,
|
|
2483
|
+
...secretLinksFor(
|
|
2484
|
+
'web-server',
|
|
2485
|
+
performanceDeploymentBinding
|
|
2486
|
+
? []
|
|
2487
|
+
: [performanceHmacLogicalName],
|
|
2488
|
+
),
|
|
2489
|
+
]);
|
|
2490
|
+
Object.assign(args, webServerLogging);
|
|
2491
|
+
args.transform = {
|
|
2492
|
+
...webServerLogging.transform,
|
|
2493
|
+
...($dev ? { role: { tags: { 'ebk:dev-runtime': 'web' } } } : {}),
|
|
2494
|
+
};
|
|
2007
2495
|
},
|
|
2008
2496
|
},
|
|
2009
|
-
vpc: foundation.vpc
|
|
2497
|
+
${sql ? ' vpc: foundation.vpc,\n' : ''}\
|
|
2010
2498
|
}, configurationDeployment
|
|
2011
2499
|
? { dependsOn: [configurationDeployment] }
|
|
2012
2500
|
: {});
|
|
@@ -2027,7 +2515,15 @@ export function product(workloadRoles?: StandardSliceWorkloadRolePreflight) {
|
|
|
2027
2515
|
}
|
|
2028
2516
|
|
|
2029
2517
|
const migrationControls: Record<string, any> = {};
|
|
2030
|
-
|
|
2518
|
+
${` Object.assign(migrationControls, createKeyValueRetentionInfrastructure({
|
|
2519
|
+
keyValue, workspace: '${workspace}', eventBusArn: foundation.discovery.eventBusArn,
|
|
2520
|
+
alertTopicArn: foundation.discovery.alertTopicArn, schedule: backgroundSchedules.retention,
|
|
2521
|
+
scheduleName: runtimeResourceNames.dataRetentionMaintenanceRule,
|
|
2522
|
+
managedLogGroup, lambdaLogging, register: registerProductRetention,
|
|
2523
|
+
}));
|
|
2524
|
+
`}\
|
|
2525
|
+
${sql
|
|
2526
|
+
? ` if (!$dev) {
|
|
2031
2527
|
const region = aws.getRegionOutput({}).name;
|
|
2032
2528
|
const taskArguments = (task: any) => ({
|
|
2033
2529
|
Cluster: task.cluster,
|
|
@@ -2049,33 +2545,38 @@ export function product(workloadRoles?: StandardSliceWorkloadRolePreflight) {
|
|
|
2049
2545
|
Environment: [
|
|
2050
2546
|
{
|
|
2051
2547
|
Name: 'EBK_DATABASE_LIFECYCLE_CORRELATION_ID',
|
|
2052
|
-
Value:
|
|
2548
|
+
Value:
|
|
2549
|
+
'{% $states.context.Execution.Input.databaseLifecycleCorrelationId %}',
|
|
2053
2550
|
},
|
|
2054
2551
|
...(callback
|
|
2055
2552
|
? [
|
|
2056
2553
|
{
|
|
2057
2554
|
Name: 'EBK_CANDIDATE_IDENTITY',
|
|
2058
|
-
Value:
|
|
2555
|
+
Value:
|
|
2556
|
+
'{% $states.context.Execution.Input.candidateIdentity %}',
|
|
2059
2557
|
},
|
|
2060
2558
|
{
|
|
2061
2559
|
Name: 'EBK_EXECUTION_DATABASE_IDENTIFIER',
|
|
2062
|
-
Value:
|
|
2560
|
+
Value:
|
|
2561
|
+
'{% $states.context.Execution.Input.databaseIdentifier %}',
|
|
2063
2562
|
},
|
|
2064
2563
|
{
|
|
2065
2564
|
Name: 'EBK_EXECUTION_SLICE',
|
|
2066
|
-
Value: '{% $states.
|
|
2565
|
+
Value: '{% $states.context.Execution.Input.slice %}',
|
|
2067
2566
|
},
|
|
2068
2567
|
{
|
|
2069
2568
|
Name: 'EBK_EXECUTION_STAGE',
|
|
2070
|
-
Value: '{% $states.
|
|
2569
|
+
Value: '{% $states.context.Execution.Input.stage %}',
|
|
2071
2570
|
},
|
|
2072
2571
|
{
|
|
2073
2572
|
Name: 'EBK_REGISTRATION_DIGEST',
|
|
2074
|
-
Value:
|
|
2573
|
+
Value:
|
|
2574
|
+
'{% $states.context.Execution.Input.registrationDigest %}',
|
|
2075
2575
|
},
|
|
2076
2576
|
{
|
|
2077
2577
|
Name: 'EBK_SOURCE_COMMIT',
|
|
2078
|
-
Value:
|
|
2578
|
+
Value:
|
|
2579
|
+
'{% $states.context.Execution.Input.sourceCommit %}',
|
|
2079
2580
|
},
|
|
2080
2581
|
{
|
|
2081
2582
|
Name: 'EBK_STEP_FUNCTIONS_TASK_TOKEN',
|
|
@@ -2091,328 +2592,25 @@ export function product(workloadRoles?: StandardSliceWorkloadRolePreflight) {
|
|
|
2091
2592
|
logicalName: string,
|
|
2092
2593
|
definition: unknown,
|
|
2093
2594
|
logGroup: any,
|
|
2094
|
-
|
|
2095
|
-
) =>
|
|
2096
|
-
|
|
2595
|
+
roleResource: string,
|
|
2596
|
+
) => {
|
|
2597
|
+
const role = nativeAuthority.createStepFunctionsRole(
|
|
2598
|
+
logicalName + 'Role',
|
|
2599
|
+
roleResource,
|
|
2600
|
+
);
|
|
2601
|
+
return new aws.sfn.StateMachine(logicalName, {
|
|
2097
2602
|
definition: $jsonStringify(definition),
|
|
2098
2603
|
loggingConfiguration: {
|
|
2099
2604
|
includeExecutionData: false,
|
|
2100
2605
|
level: 'ERROR',
|
|
2101
2606
|
logDestination: $interpolate\`\${logGroup.arn}:*\`,
|
|
2102
2607
|
},
|
|
2103
|
-
roleArn,
|
|
2608
|
+
roleArn: role.arn,
|
|
2104
2609
|
type: 'STANDARD',
|
|
2105
2610
|
});
|
|
2106
|
-
|
|
2107
|
-
if (!databaseProvision.readwriteSecretArn) {
|
|
2108
|
-
throw new Error(
|
|
2109
|
-
'The active primary database is missing its read/write credential reference.',
|
|
2110
|
-
);
|
|
2111
|
-
}
|
|
2112
|
-
const retentionTaskLogs = managedLogContract('job-data-retention');
|
|
2113
|
-
const retentionTask = new sst.aws.Task('${pascalName}DataRetentionTask', {
|
|
2114
|
-
...workloadRoleBindings!.dataRetention.task,
|
|
2115
|
-
cluster: foundation.cluster,
|
|
2116
|
-
environment: {
|
|
2117
|
-
...dataRetentionContainer.environment,
|
|
2118
|
-
AWS_REGION: region,
|
|
2119
|
-
EBK_DATABASE_HOST: foundation.database.clusterEndpoint,
|
|
2120
|
-
EBK_DATABASE_NAME: databaseProvision.databaseName,
|
|
2121
|
-
EBK_DATABASE_PORT: $interpolate\`\${foundation.database.port}\`,
|
|
2122
|
-
EBK_DATABASE_USERNAME: databaseProvision.readwriteRole,
|
|
2123
|
-
EBK_RDS_CA_BUNDLE: '/app/rds-global-bundle.pem',
|
|
2124
|
-
EBK_READWRITE_SECRET_ARN: databaseProvision.readwriteSecretArn,
|
|
2125
|
-
EBK_SLICE: '${name}',
|
|
2126
|
-
EBK_STAGE: $app.stage,
|
|
2127
|
-
EBK_WORKLOAD_IDENTITY: '${name}:data-retention-job',
|
|
2128
|
-
EBK_WORKSPACE: '${workspace}',
|
|
2129
|
-
EVENT_BUS_NAME: foundation.eventBus.name,
|
|
2130
|
-
},
|
|
2131
|
-
image: dataRetentionContainer.image,
|
|
2132
|
-
link: secretLinksFor('data-retention-job-task'),
|
|
2133
|
-
logging: {
|
|
2134
|
-
name: retentionTaskLogs.name,
|
|
2135
|
-
retention: '1 month',
|
|
2136
|
-
},
|
|
2137
|
-
transform: {
|
|
2138
|
-
logGroup: {
|
|
2139
|
-
logGroupClass: retentionTaskLogs.logGroupClass,
|
|
2140
|
-
tags: retentionTaskLogs.tags,
|
|
2141
|
-
},
|
|
2142
|
-
taskDefinition: {
|
|
2143
|
-
family: workloadRoleResourceNames!.dataRetentionTaskFamily,
|
|
2144
|
-
},
|
|
2145
|
-
},
|
|
2146
|
-
});
|
|
2147
|
-
const retentionTaskLogDataProtection = managedLogDataProtection(
|
|
2148
|
-
'${pascalName}DataRetentionTaskLog',
|
|
2149
|
-
retentionTaskLogs,
|
|
2150
|
-
retentionTaskLogs.name,
|
|
2151
|
-
[retentionTask.nodes.taskDefinition],
|
|
2152
|
-
foundation.discovery.alertTopicArn,
|
|
2153
|
-
);
|
|
2154
|
-
const retentionWorkflowLogs = managedLogGroup(
|
|
2155
|
-
'${pascalName}DataRetentionWorkflowLogs',
|
|
2156
|
-
'job-data-retention-orchestration',
|
|
2157
|
-
foundation.discovery.alertTopicArn,
|
|
2158
|
-
);
|
|
2159
|
-
const retentionOverrides = retentionTask.containers.apply(
|
|
2160
|
-
(containers: string[]) => ({
|
|
2161
|
-
ContainerOverrides: containers.map((containerName) => ({
|
|
2162
|
-
Environment: [
|
|
2163
|
-
{
|
|
2164
|
-
Name: 'EBK_RETENTION_APPROVAL_ID',
|
|
2165
|
-
Value: '{% $states.input.approvalRequestId %}',
|
|
2166
|
-
},
|
|
2167
|
-
{
|
|
2168
|
-
Name: 'EBK_RETENTION_CORRELATION_ID',
|
|
2169
|
-
Value: '{% $states.input.correlationId %}',
|
|
2170
|
-
},
|
|
2171
|
-
{
|
|
2172
|
-
Name: 'EBK_RETENTION_AS_OF',
|
|
2173
|
-
Value: '{% $states.input.asOf %}',
|
|
2174
|
-
},
|
|
2175
|
-
{
|
|
2176
|
-
Name: 'EBK_RETENTION_MODE',
|
|
2177
|
-
Value: '{% $states.input.mode %}',
|
|
2178
|
-
},
|
|
2179
|
-
{
|
|
2180
|
-
Name: 'EBK_RETENTION_RUN_ID',
|
|
2181
|
-
Value: '{% $states.input.runId %}',
|
|
2182
|
-
},
|
|
2183
|
-
{
|
|
2184
|
-
Name: 'EBK_STEP_FUNCTIONS_TASK_TOKEN',
|
|
2185
|
-
Value: '{% $states.context.Task.Token %}',
|
|
2186
|
-
},
|
|
2187
|
-
],
|
|
2188
|
-
Name: containerName,
|
|
2189
|
-
})),
|
|
2190
|
-
}),
|
|
2191
|
-
);
|
|
2192
|
-
const retentionStateMachine = createRawStateMachine(
|
|
2193
|
-
'${pascalName}DataRetention',
|
|
2194
|
-
{
|
|
2195
|
-
StartAt: 'RunDataRetention',
|
|
2196
|
-
States: {
|
|
2197
|
-
DataRetentionSucceeded: {
|
|
2198
|
-
QueryLanguage: 'JSONata',
|
|
2199
|
-
Type: 'Succeed',
|
|
2200
|
-
},
|
|
2201
|
-
RunDataRetention: {
|
|
2202
|
-
Arguments: {
|
|
2203
|
-
...taskArguments(retentionTask),
|
|
2204
|
-
Overrides: retentionOverrides,
|
|
2205
|
-
},
|
|
2206
|
-
Next: 'DataRetentionSucceeded',
|
|
2207
|
-
QueryLanguage: 'JSONata',
|
|
2208
|
-
Resource: 'arn:aws:states:::ecs:runTask.waitForTaskToken',
|
|
2209
|
-
Retry: [
|
|
2210
|
-
{
|
|
2211
|
-
BackoffRate: 2,
|
|
2212
|
-
ErrorEquals: [
|
|
2213
|
-
'ECS.AmazonECSException',
|
|
2214
|
-
'ECS.ECSException',
|
|
2215
|
-
],
|
|
2216
|
-
IntervalSeconds: 10,
|
|
2217
|
-
MaxAttempts: 2,
|
|
2218
|
-
},
|
|
2219
|
-
],
|
|
2220
|
-
TimeoutSeconds: 2700,
|
|
2221
|
-
Type: 'Task',
|
|
2222
|
-
},
|
|
2223
|
-
},
|
|
2224
|
-
},
|
|
2225
|
-
retentionWorkflowLogs.logGroup,
|
|
2226
|
-
workloadRoleBindings!.dataRetention.orchestration.roleArn,
|
|
2227
|
-
);
|
|
2228
|
-
const retentionFailureAlarm = new aws.cloudwatch.MetricAlarm(
|
|
2229
|
-
'${pascalName}DataRetentionFailure',
|
|
2230
|
-
{
|
|
2231
|
-
alarmActions: [foundation.discovery.alertTopicArn],
|
|
2232
|
-
alarmDescription: operationalAlarmDescription({
|
|
2233
|
-
businessSymptom:
|
|
2234
|
-
'The built-in retention Job terminated unsuccessfully.',
|
|
2235
|
-
id: 'retention-job-failure',
|
|
2236
|
-
runbook: 'docs/runbooks/database-recovery.md',
|
|
2237
|
-
}),
|
|
2238
|
-
comparisonOperator: 'GreaterThanOrEqualToThreshold',
|
|
2239
|
-
evaluationPeriods: 1,
|
|
2240
|
-
metricQueries: [
|
|
2241
|
-
...['ExecutionsFailed', 'ExecutionsTimedOut', 'ExecutionsAborted'].map(
|
|
2242
|
-
(metricName, index) => ({
|
|
2243
|
-
id: 'm' + index,
|
|
2244
|
-
metric: {
|
|
2245
|
-
dimensions: {
|
|
2246
|
-
StateMachineArn: retentionStateMachine.arn,
|
|
2247
|
-
},
|
|
2248
|
-
metricName,
|
|
2249
|
-
namespace: 'AWS/States',
|
|
2250
|
-
period: 60,
|
|
2251
|
-
stat: 'Sum',
|
|
2252
|
-
},
|
|
2253
|
-
returnData: false,
|
|
2254
|
-
}),
|
|
2255
|
-
),
|
|
2256
|
-
{
|
|
2257
|
-
expression: 'm0+m1+m2',
|
|
2258
|
-
id: 'failures',
|
|
2259
|
-
label: 'Retention terminal execution failures',
|
|
2260
|
-
returnData: true,
|
|
2261
|
-
},
|
|
2262
|
-
],
|
|
2263
|
-
name: '${name}-' + $app.stage + '-retention-job-failure',
|
|
2264
|
-
threshold: 1,
|
|
2265
|
-
treatMissingData: 'notBreaching',
|
|
2266
|
-
},
|
|
2267
|
-
);
|
|
2268
|
-
const retentionRegistration = registerProductRetention(
|
|
2269
|
-
retentionStateMachine.arn,
|
|
2270
|
-
);
|
|
2271
|
-
migrationControls.dataRetention = {
|
|
2272
|
-
registration: retentionRegistration,
|
|
2273
|
-
retentionFailureAlarm,
|
|
2274
|
-
retentionStateMachine,
|
|
2275
|
-
retentionTask,
|
|
2276
|
-
retentionTaskLogDataProtection,
|
|
2277
|
-
retentionWorkflowLogs,
|
|
2278
|
-
};
|
|
2279
|
-
const retentionMaintenanceTaskLogs = managedLogContract(
|
|
2280
|
-
'job-data-retention-maintenance',
|
|
2281
|
-
);
|
|
2282
|
-
const retentionMaintenanceTask = new sst.aws.Task(
|
|
2283
|
-
'${pascalName}DataRetentionMaintenanceTask',
|
|
2284
|
-
{
|
|
2285
|
-
...workloadRoleBindings!.dataRetention.maintenance.task,
|
|
2286
|
-
cluster: foundation.cluster,
|
|
2287
|
-
environment: {
|
|
2288
|
-
...dataRetentionContainer.environment,
|
|
2289
|
-
AWS_REGION: region,
|
|
2290
|
-
EBK_DATABASE_HOST: foundation.database.clusterEndpoint,
|
|
2291
|
-
EBK_DATABASE_NAME: databaseProvision.databaseName,
|
|
2292
|
-
EBK_DATABASE_PORT: $interpolate\`\${foundation.database.port}\`,
|
|
2293
|
-
EBK_DATABASE_USERNAME: databaseProvision.readwriteRole,
|
|
2294
|
-
EBK_RDS_CA_BUNDLE: '/app/rds-global-bundle.pem',
|
|
2295
|
-
EBK_READWRITE_SECRET_ARN:
|
|
2296
|
-
databaseProvision.readwriteSecretArn,
|
|
2297
|
-
EBK_SLICE: '${name}',
|
|
2298
|
-
EBK_STAGE: $app.stage,
|
|
2299
|
-
EBK_WORKLOAD_IDENTITY: '${name}:data-retention-job',
|
|
2300
|
-
EBK_WORKSPACE: '${workspace}',
|
|
2301
|
-
},
|
|
2302
|
-
image: dataRetentionContainer.image,
|
|
2303
|
-
link: secretLinksFor(
|
|
2304
|
-
'data-retention-maintenance-job-task',
|
|
2305
|
-
),
|
|
2306
|
-
logging: {
|
|
2307
|
-
name: retentionMaintenanceTaskLogs.name,
|
|
2308
|
-
retention: '1 month',
|
|
2309
|
-
},
|
|
2310
|
-
transform: {
|
|
2311
|
-
logGroup: {
|
|
2312
|
-
logGroupClass:
|
|
2313
|
-
retentionMaintenanceTaskLogs.logGroupClass,
|
|
2314
|
-
tags: retentionMaintenanceTaskLogs.tags,
|
|
2315
|
-
},
|
|
2316
|
-
taskDefinition: {
|
|
2317
|
-
family:
|
|
2318
|
-
workloadRoleResourceNames!
|
|
2319
|
-
.dataRetentionMaintenanceTaskFamily,
|
|
2320
|
-
},
|
|
2321
|
-
},
|
|
2322
|
-
},
|
|
2323
|
-
);
|
|
2324
|
-
const retentionMaintenanceTaskLogDataProtection =
|
|
2325
|
-
managedLogDataProtection(
|
|
2326
|
-
'${pascalName}DataRetentionMaintenanceTaskLog',
|
|
2327
|
-
retentionMaintenanceTaskLogs,
|
|
2328
|
-
retentionMaintenanceTaskLogs.name,
|
|
2329
|
-
[retentionMaintenanceTask.nodes.taskDefinition],
|
|
2330
|
-
foundation.discovery.alertTopicArn,
|
|
2331
|
-
);
|
|
2332
|
-
const retentionMaintenanceLauncherLogs = managedLogGroup(
|
|
2333
|
-
'${pascalName}DataRetentionMaintenanceLauncherLogs',
|
|
2334
|
-
'job-data-retention-maintenance-launcher',
|
|
2335
|
-
foundation.discovery.alertTopicArn,
|
|
2336
|
-
);
|
|
2337
|
-
const retentionMaintenanceContainerName =
|
|
2338
|
-
retentionMaintenanceTask.containers.apply(
|
|
2339
|
-
(containers: string[]) => {
|
|
2340
|
-
if (containers.length !== 1 || !containers[0]) {
|
|
2341
|
-
throw new Error(
|
|
2342
|
-
'Retention maintenance requires exactly one task container.',
|
|
2343
|
-
);
|
|
2344
|
-
}
|
|
2345
|
-
return containers[0];
|
|
2346
|
-
},
|
|
2347
|
-
);
|
|
2348
|
-
const retentionMaintenanceLauncher = new sst.aws.Function(
|
|
2349
|
-
'${pascalName}DataRetentionMaintenanceLauncher',
|
|
2350
|
-
{
|
|
2351
|
-
...workloadRoleBindings!.dataRetention.maintenance.launcher,
|
|
2352
|
-
environment: {
|
|
2353
|
-
EBK_MAINTENANCE_ASSIGN_PUBLIC_IP:
|
|
2354
|
-
retentionMaintenanceTask.assignPublicIp.apply(
|
|
2355
|
-
(value: boolean) => String(value),
|
|
2356
|
-
),
|
|
2357
|
-
EBK_MAINTENANCE_CLUSTER_ARN:
|
|
2358
|
-
retentionMaintenanceTask.cluster,
|
|
2359
|
-
EBK_MAINTENANCE_CONTAINER_NAME:
|
|
2360
|
-
retentionMaintenanceContainerName,
|
|
2361
|
-
EBK_MAINTENANCE_SECURITY_GROUP_IDS: $jsonStringify(
|
|
2362
|
-
retentionMaintenanceTask.securityGroups,
|
|
2363
|
-
),
|
|
2364
|
-
EBK_MAINTENANCE_SUBNET_IDS: $jsonStringify(
|
|
2365
|
-
retentionMaintenanceTask.subnets,
|
|
2366
|
-
),
|
|
2367
|
-
EBK_MAINTENANCE_TASK_DEFINITION_ARN:
|
|
2368
|
-
retentionMaintenanceTask.taskDefinition,
|
|
2369
|
-
EBK_STAGE: $app.stage,
|
|
2370
|
-
EBK_WORKSPACE: '${workspace}',
|
|
2371
|
-
},
|
|
2372
|
-
handler: 'jobs/data-retention-maintenance-launcher.handler',
|
|
2373
|
-
...lambdaLogging(
|
|
2374
|
-
retentionMaintenanceLauncherLogs.logGroup.name,
|
|
2375
|
-
),
|
|
2376
|
-
timeout: '1 minute',
|
|
2377
|
-
},
|
|
2378
|
-
);
|
|
2379
|
-
const retentionMaintenanceRule = new aws.cloudwatch.EventRule(
|
|
2380
|
-
'${pascalName}DataRetentionMaintenanceSchedule',
|
|
2381
|
-
{
|
|
2382
|
-
name:
|
|
2383
|
-
workloadRoleResourceNames!.dataRetentionMaintenanceRule,
|
|
2384
|
-
scheduleExpression: 'rate(1 day)',
|
|
2385
|
-
},
|
|
2386
|
-
);
|
|
2387
|
-
const retentionMaintenancePermission =
|
|
2388
|
-
new aws.lambda.Permission(
|
|
2389
|
-
'${pascalName}DataRetentionMaintenanceSchedulePermission',
|
|
2390
|
-
{
|
|
2391
|
-
action: 'lambda:InvokeFunction',
|
|
2392
|
-
function: retentionMaintenanceLauncher.arn,
|
|
2393
|
-
principal: 'events.amazonaws.com',
|
|
2394
|
-
sourceArn: retentionMaintenanceRule.arn,
|
|
2395
|
-
},
|
|
2396
|
-
);
|
|
2397
|
-
const retentionMaintenanceTarget =
|
|
2398
|
-
new aws.cloudwatch.EventTarget(
|
|
2399
|
-
'${pascalName}DataRetentionMaintenanceTarget',
|
|
2400
|
-
{
|
|
2401
|
-
arn: retentionMaintenanceLauncher.arn,
|
|
2402
|
-
rule: retentionMaintenanceRule.name,
|
|
2403
|
-
targetId: 'DataRetentionMaintenance',
|
|
2404
|
-
},
|
|
2405
|
-
{ dependsOn: [retentionMaintenancePermission] },
|
|
2406
|
-
);
|
|
2407
|
-
migrationControls.dataRetentionMaintenance = {
|
|
2408
|
-
retentionMaintenanceLauncher,
|
|
2409
|
-
retentionMaintenanceLauncherLogs,
|
|
2410
|
-
retentionMaintenanceRule,
|
|
2411
|
-
retentionMaintenanceTarget,
|
|
2412
|
-
retentionMaintenanceTask,
|
|
2413
|
-
retentionMaintenanceTaskLogDataProtection,
|
|
2414
2611
|
};
|
|
2415
2612
|
|
|
2613
|
+
${''}\
|
|
2416
2614
|
for (const [databaseIdentifier, target] of Object.entries(
|
|
2417
2615
|
databaseProvisions.databases,
|
|
2418
2616
|
)) {
|
|
@@ -2434,12 +2632,12 @@ export function product(workloadRoles?: StandardSliceWorkloadRolePreflight) {
|
|
|
2434
2632
|
'database-retirement',
|
|
2435
2633
|
databaseIdentifier,
|
|
2436
2634
|
);
|
|
2437
|
-
const databaseRoleBindings =
|
|
2438
|
-
workloadRoleBindings!.database(databaseIdentifier);
|
|
2439
2635
|
const bootstrapTask = new sst.aws.Task(
|
|
2440
2636
|
target.resourceName + 'BootstrapTask',
|
|
2441
2637
|
{
|
|
2442
|
-
|
|
2638
|
+
permissions: nativeAuthority.permissions(
|
|
2639
|
+
'database-' + databaseIdentifier + '-bootstrap-task',
|
|
2640
|
+
),
|
|
2443
2641
|
cluster: foundation.cluster,
|
|
2444
2642
|
command: ['node', 'jobs/bootstrap-database.mjs'],
|
|
2445
2643
|
environment: {
|
|
@@ -2496,7 +2694,9 @@ export function product(workloadRoles?: StandardSliceWorkloadRolePreflight) {
|
|
|
2496
2694
|
const migrationTask = new sst.aws.Task(
|
|
2497
2695
|
target.resourceName + 'MigrationTask',
|
|
2498
2696
|
{
|
|
2499
|
-
|
|
2697
|
+
permissions: nativeAuthority.permissions(
|
|
2698
|
+
'database-' + databaseIdentifier + '-migration-task',
|
|
2699
|
+
),
|
|
2500
2700
|
cluster: foundation.cluster,
|
|
2501
2701
|
environment: {
|
|
2502
2702
|
...databaseMigrationContainer.environment,
|
|
@@ -2562,6 +2762,18 @@ export function product(workloadRoles?: StandardSliceWorkloadRolePreflight) {
|
|
|
2562
2762
|
Next: 'RunDatabaseMigration',
|
|
2563
2763
|
QueryLanguage: 'JSONata',
|
|
2564
2764
|
Resource: 'arn:aws:states:::ecs:runTask.sync',
|
|
2765
|
+
// The first bootstrap of a Slice runs minutes after its execution
|
|
2766
|
+
// role is created; ECR can still answer "not found" to the fresh
|
|
2767
|
+
// role and the task never starts (EBK-INT-2026-072). Bootstrap is
|
|
2768
|
+
// idempotent, so a bounded retry is safe.
|
|
2769
|
+
Retry: [
|
|
2770
|
+
{
|
|
2771
|
+
BackoffRate: 2,
|
|
2772
|
+
ErrorEquals: ['States.TaskFailed'],
|
|
2773
|
+
IntervalSeconds: 30,
|
|
2774
|
+
MaxAttempts: 3,
|
|
2775
|
+
},
|
|
2776
|
+
],
|
|
2565
2777
|
TimeoutSeconds: 2700,
|
|
2566
2778
|
Type: 'Task',
|
|
2567
2779
|
},
|
|
@@ -2583,7 +2795,7 @@ export function product(workloadRoles?: StandardSliceWorkloadRolePreflight) {
|
|
|
2583
2795
|
},
|
|
2584
2796
|
},
|
|
2585
2797
|
migrationWorkflowLogs.logGroup,
|
|
2586
|
-
|
|
2798
|
+
'database-' + databaseIdentifier + '-migration-orchestration',
|
|
2587
2799
|
);
|
|
2588
2800
|
const registration = registerProductMigration(
|
|
2589
2801
|
target.resourceName + 'MigrationRegistration',
|
|
@@ -2592,10 +2804,11 @@ export function product(workloadRoles?: StandardSliceWorkloadRolePreflight) {
|
|
|
2592
2804
|
);
|
|
2593
2805
|
const retirementTask =
|
|
2594
2806
|
$app.stage === 'development' &&
|
|
2595
|
-
databaseIdentifier === 'retirement_rehearsal'
|
|
2596
|
-
databaseRoleBindings.retirement
|
|
2807
|
+
databaseIdentifier === 'retirement_rehearsal'
|
|
2597
2808
|
? new sst.aws.Task(target.resourceName + 'RetirementTask', {
|
|
2598
|
-
|
|
2809
|
+
permissions: nativeAuthority.permissions(
|
|
2810
|
+
'database-' + databaseIdentifier + '-retirement-task',
|
|
2811
|
+
),
|
|
2599
2812
|
cluster: foundation.cluster,
|
|
2600
2813
|
command: ['node', 'jobs/retire-database.mjs'],
|
|
2601
2814
|
environment: {
|
|
@@ -2709,7 +2922,7 @@ export function product(workloadRoles?: StandardSliceWorkloadRolePreflight) {
|
|
|
2709
2922
|
},
|
|
2710
2923
|
},
|
|
2711
2924
|
retirementWorkflowLogs.logGroup,
|
|
2712
|
-
|
|
2925
|
+
'database-' + databaseIdentifier + '-retirement-orchestration',
|
|
2713
2926
|
);
|
|
2714
2927
|
retirementControl = {
|
|
2715
2928
|
retirementWorkflowLogs,
|
|
@@ -2731,12 +2944,15 @@ export function product(workloadRoles?: StandardSliceWorkloadRolePreflight) {
|
|
|
2731
2944
|
}
|
|
2732
2945
|
}
|
|
2733
2946
|
|
|
2947
|
+
`
|
|
2948
|
+
: ''}\
|
|
2734
2949
|
const outboxRelayAlarm = new aws.cloudwatch.MetricAlarm(
|
|
2735
2950
|
'${pascalName}OutboxRelayErrors',
|
|
2736
2951
|
{
|
|
2737
2952
|
alarmActions: [foundation.discovery.alertTopicArn],
|
|
2738
2953
|
alarmDescription: operationalAlarmDescription({
|
|
2739
|
-
businessSymptom:
|
|
2954
|
+
businessSymptom:
|
|
2955
|
+
'The isolated performance-sink outbox relay is failing.',
|
|
2740
2956
|
id: 'outbox-relay-errors',
|
|
2741
2957
|
runbook: 'docs/runbooks/async-recovery.md',
|
|
2742
2958
|
}),
|
|
@@ -2821,27 +3037,6 @@ export function product(workloadRoles?: StandardSliceWorkloadRolePreflight) {
|
|
|
2821
3037
|
treatMissingData: 'breaching',
|
|
2822
3038
|
},
|
|
2823
3039
|
);
|
|
2824
|
-
const outboxBacklogAlarm = new aws.cloudwatch.MetricAlarm(
|
|
2825
|
-
'${pascalName}OutboxBacklog',
|
|
2826
|
-
{
|
|
2827
|
-
alarmActions: [foundation.discovery.alertTopicArn],
|
|
2828
|
-
alarmDescription: operationalAlarmDescription({
|
|
2829
|
-
businessSymptom: 'Transactional outbox work is accumulating.',
|
|
2830
|
-
id: 'outbox-backlog',
|
|
2831
|
-
runbook: 'docs/runbooks/async-recovery.md',
|
|
2832
|
-
}),
|
|
2833
|
-
comparisonOperator: 'GreaterThanOrEqualToThreshold',
|
|
2834
|
-
dimensions: { Slice: '${name}', Stage: $app.stage },
|
|
2835
|
-
evaluationPeriods: 2,
|
|
2836
|
-
metricName: 'OutboxBacklog',
|
|
2837
|
-
name: '${name}-' + $app.stage + '-outbox-backlog',
|
|
2838
|
-
namespace: 'EBK/Async',
|
|
2839
|
-
period: 300,
|
|
2840
|
-
statistic: 'Maximum',
|
|
2841
|
-
threshold: 1,
|
|
2842
|
-
treatMissingData: 'notBreaching',
|
|
2843
|
-
},
|
|
2844
|
-
);
|
|
2845
3040
|
const inboxCleanupBacklogAlarm = new aws.cloudwatch.MetricAlarm(
|
|
2846
3041
|
'${pascalName}InboxCleanupBacklog',
|
|
2847
3042
|
{
|
|
@@ -2987,6 +3182,7 @@ export function product(workloadRoles?: StandardSliceWorkloadRolePreflight) {
|
|
|
2987
3182
|
performanceLoadCleanupBacklogAlarm,
|
|
2988
3183
|
performanceLoadCleanupDeadlineAlarm,
|
|
2989
3184
|
asyncOutboxRelay,
|
|
3185
|
+
${sql ? ' sqlOutbox,\n sqlOutboxAttemptsAlarm,\n sqlOutboxBacklogAlarm,\n sqlOutboxRelayErrorAlarm,\n' : ''}\
|
|
2990
3186
|
asyncRecoveryLedger,
|
|
2991
3187
|
asyncScheduleTarget,
|
|
2992
3188
|
asyncTargetDlq,
|
|
@@ -3006,12 +3202,18 @@ export function product(workloadRoles?: StandardSliceWorkloadRolePreflight) {
|
|
|
3006
3202
|
dataExportCleanupRule,
|
|
3007
3203
|
dataExportCleanupTarget,
|
|
3008
3204
|
dataExportStore,
|
|
3009
|
-
database
|
|
3205
|
+
${sql ? ' database,\n' : ''}\
|
|
3206
|
+
${' keyValue,\n'}\
|
|
3010
3207
|
dlqAlarm,
|
|
3011
3208
|
eventDlq,
|
|
3012
3209
|
eventQueue,
|
|
3013
3210
|
machineClientProvisioner,
|
|
3014
3211
|
migrationControls,
|
|
3212
|
+
eventDeliveryAttemptsAlarm,
|
|
3213
|
+
eventIntentRejectedAlarm,
|
|
3214
|
+
keyValueReconciliationErrorAlarm,
|
|
3215
|
+
keyValueStreamDeliveryErrorAlarm,
|
|
3216
|
+
keyValueStreamIteratorAgeAlarm,
|
|
3015
3217
|
outboxBacklogAlarm,
|
|
3016
3218
|
outboxRelayAlarm,
|
|
3017
3219
|
outboxRelayRule,
|
|
@@ -3034,5 +3236,7 @@ export function product(workloadRoles?: StandardSliceWorkloadRolePreflight) {
|
|
|
3034
3236
|
}
|
|
3035
3237
|
`,
|
|
3036
3238
|
};
|
|
3239
|
+
Object.assign(files, (0, built_in_plan_key_value_retention_infrastructure_js_1.renderKeyValueRetentionInfrastructureFiles)(name, pascalName));
|
|
3240
|
+
return files;
|
|
3037
3241
|
}
|
|
3038
3242
|
//# sourceMappingURL=built-in-plan-infrastructure-files.js.map
|