@fjall/components-infrastructure 33.0.0 → 34.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/README.md +15 -76
  2. package/dist/lib/app.d.ts +1 -1
  3. package/dist/lib/config/aws/identityCentreConfig.d.ts +1 -1
  4. package/dist/lib/config/aws/identityCentreConfig.js +1 -1
  5. package/dist/lib/config/aws/scpPreset.js +12 -3
  6. package/dist/lib/lambda-assets/cert-generator/asset/index.js +126 -0
  7. package/dist/lib/patterns/aws/bastionFactory.js +2 -1
  8. package/dist/lib/patterns/aws/buildkite/buildkite.d.ts +1 -1
  9. package/dist/lib/patterns/aws/buildkite/buildkite.js +1 -1
  10. package/dist/lib/patterns/aws/buildkite/factory.d.ts +2 -2
  11. package/dist/lib/patterns/aws/buildkite/pins.d.ts +2 -2
  12. package/dist/lib/patterns/aws/buildkite/pins.js +2 -2
  13. package/dist/lib/patterns/aws/buildkite/schema.d.ts +1 -1
  14. package/dist/lib/patterns/aws/buildkite/schema.js +1 -1
  15. package/dist/lib/patterns/aws/buildkite/userData.d.ts +1 -1
  16. package/dist/lib/patterns/aws/buildkite/userData.js +1 -1
  17. package/dist/lib/patterns/aws/buildkite.d.ts +1 -1
  18. package/dist/lib/patterns/aws/buildkite.js +1 -1
  19. package/dist/lib/patterns/aws/cdn.d.ts +4 -1
  20. package/dist/lib/patterns/aws/clickhouseDatabase.js +6 -5
  21. package/dist/lib/patterns/aws/clickhouseTls/types.d.ts +1 -1
  22. package/dist/lib/patterns/aws/compute.d.ts +2 -2
  23. package/dist/lib/patterns/aws/computeEc2.d.ts +5 -18
  24. package/dist/lib/patterns/aws/computeEc2.js +9 -3
  25. package/dist/lib/patterns/aws/computeEcs.d.ts +4 -4
  26. package/dist/lib/patterns/aws/computeEcs.js +5 -9
  27. package/dist/lib/patterns/aws/computeEcsTypes.d.ts +5 -5
  28. package/dist/lib/patterns/aws/computePropApplicability.js +1 -1
  29. package/dist/lib/patterns/aws/fivetranProxy.d.ts +8 -3
  30. package/dist/lib/patterns/aws/fivetranProxy.js +9 -6
  31. package/dist/lib/patterns/aws/interfaces/compute.d.ts +8 -0
  32. package/dist/lib/patterns/aws/payload.js +6 -2
  33. package/dist/lib/patterns/aws/storage.d.ts +8 -0
  34. package/dist/lib/patterns/aws/storage.js +1 -0
  35. package/dist/lib/resources/aws/cdn/cloudFront.d.ts +16 -0
  36. package/dist/lib/resources/aws/cdn/cloudFront.js +52 -2
  37. package/dist/lib/resources/aws/cdn/responseHeadersPolicy.d.ts +1 -1
  38. package/dist/lib/resources/aws/cdn/responseHeadersPolicy.js +1 -1
  39. package/dist/lib/resources/aws/compute/ec2.d.ts +35 -8
  40. package/dist/lib/resources/aws/compute/ec2.js +64 -15
  41. package/dist/lib/resources/aws/compute/ec2SshAccess.d.ts +80 -0
  42. package/dist/lib/resources/aws/compute/ec2SshAccess.js +166 -0
  43. package/dist/lib/resources/aws/compute/ecs.d.ts +11 -2
  44. package/dist/lib/resources/aws/compute/ecs.js +29 -6
  45. package/dist/lib/resources/aws/compute/lambda.js +1 -3
  46. package/dist/lib/resources/aws/database/clickhouseConstants.d.ts +5 -5
  47. package/dist/lib/resources/aws/database/clickhouseConstants.js +5 -5
  48. package/dist/lib/resources/aws/database/dynamodb.js +5 -6
  49. package/dist/lib/resources/aws/iam/identityCenter/user.d.ts +1 -1
  50. package/dist/lib/resources/aws/iam/identityCenter/user.js +1 -1
  51. package/dist/lib/resources/aws/logging/accessLogBucket.d.ts +5 -3
  52. package/dist/lib/resources/aws/logging/accessLogBucket.js +6 -3
  53. package/dist/lib/resources/aws/messaging/sns.d.ts +17 -7
  54. package/dist/lib/resources/aws/messaging/sns.js +4 -4
  55. package/dist/lib/resources/aws/secrets/stackEncryptionKey.d.ts +17 -4
  56. package/dist/lib/resources/aws/secrets/stackEncryptionKey.js +20 -4
  57. package/dist/lib/resources/aws/storage/bucketDeployment.d.ts +2 -3
  58. package/dist/lib/resources/aws/storage/bucketDeployment.js +2 -3
  59. package/dist/lib/resources/aws/storage/s3.d.ts +14 -1
  60. package/dist/lib/resources/aws/storage/s3.js +6 -3
  61. package/package.json +6 -4
package/README.md CHANGED
@@ -54,84 +54,23 @@ for each of the two secrets.
54
54
 
55
55
  ## Useful commands
56
56
 
57
- - `npm run build` compile typescript to js
58
- - `npm run watch` watch for changes and compile
59
- - `npm run test` perform the jest unit tests
60
- - `cdk deploy` deploy this stack to your default AWS account/region
61
- - `cdk diff` compare deployed stack with current state
62
- - `cdk synth` emits the synthesized CloudFormation template
57
+ - `npm run build` — clean, bundle the Lambda assets and compile TypeScript to `dist/`
58
+ - `npm run watch` — recompile on change
59
+ - `npm test` — run the Vitest suites
60
+ - `npm run typecheck` — type-check the library
63
61
 
64
- ## Building patterns
62
+ Synthesis and deployment go through the `fjall` CLI (`fjall deploy`), which
63
+ assembles the stacks from an application's `fjall/` directory. The package is
64
+ not deployed with `cdk deploy` on its own.
65
65
 
66
- Common service patterns are built using a Stack class (i.e AwsStack) and use a builder pattern.
66
+ ## Using the constructs
67
67
 
68
- ### Referencing VPC
68
+ The constructs are consumed through the `fjall` CLI: `fjall create app` and
69
+ `fjall add <resource>` write the `infrastructure.ts` that composes them, and
70
+ `fjall deploy` synthesises and deploys it. Every construct is also exported
71
+ from the package root for direct use; the reference lives at
72
+ [docs.fjall.io](https://docs.fjall.io).
69
73
 
70
- There are three main ways to reference a VPC
74
+ ## Licence
71
75
 
72
- 1. In the same stack
73
-
74
- When a VPC is created in the same stack resources that rely on it will automatically detect it.
75
-
76
- ```typescript
77
- new AwsStack(`${id}App`)
78
- .addNetwork(FjallVpc.build(`${id}Vpc`))
79
- .addConstruct(Fargate.build(`${id}Fargate`));
80
- ```
81
-
82
- 2. In the same CDK Application but different stack
83
-
84
- When a VPC is created in the same application, but you want to use separate stacks, you can directly reference the vpc resource itself. This is easily
85
- retrieved from an awsStack using `awsStack.getNetwork()`.
86
-
87
- ```typescript
88
- const vpcStack = new AwsStack(`${id}Network`).addNetwork(
89
- FjallVpc.build(`${id}Vpc`)
90
- );
91
-
92
- const newStack = new AwsStack(`${id}Compute`, { dependencies: [vpcStack] })
93
- .addNetwork(vpcStack.getNetwork())
94
- .addConstruct(Fargate.build(`${id}Fargate`));
95
- ```
96
-
97
- 3. Different CDK Applications
98
-
99
- If your VPC was created in a completely separate CDK Stack it can be imported using it's `id` and `stackName`. However this method
100
- requires you explicitly configure your `AwsStack` with an `account` and `region.
101
-
102
- ```typescript
103
- const env = {
104
- region: "us-east-1",
105
- account: "123456789012"
106
- };
107
-
108
- const vpcStack = new AwsStack(`${id}Network`, { ...env }).addNetwork(
109
- FjallVpc.build(`${id}Vpc`)
110
- );
111
-
112
- const newStack = new AwsStack(`${id}Compute`, {
113
- ...env,
114
- dependencies: [vpcStack]
115
- })
116
- .addNetwork(FjallVpc.import(`${id}Vpc`, `${id}Network`))
117
- .addConstruct(Fargate.build(`${id}Fargate`));
118
- ```
119
-
120
- ## Publishing package to npm
121
-
122
- Log into your NPM account locally (make sure you have 2fa setup before trying this)
123
-
124
- ```
125
- npm login
126
- > Username:
127
- > Password
128
- > Email
129
- > 2Fa
130
- ```
131
-
132
- Update the version in ./package.json, build the package and then run publish command
133
-
134
- ```
135
- npm run build
136
- npm publish --access restricted
137
- ```
76
+ Proprietary — see [LICENSE](./LICENSE).
package/dist/lib/app.d.ts CHANGED
@@ -311,7 +311,7 @@ export declare class App extends CdkApp {
311
311
  addDevSubstrate(props: IDevSubstrateProps): DevSubstrate;
312
312
  /**
313
313
  * Instantiate the self-hosted Buildkite agent fleet on the default compute
314
- * stack (design `aiDocs/designs/2026-07-18-buildkite-selfhosted-agents.md`).
314
+ * stack (design: the 2026-07-18 Buildkite self-hosted agents design).
315
315
  *
316
316
  * Same placement contract as `addDevSubstrate` above: deploy-core selects
317
317
  * stacks by the fixed six-category enum, so scoping under
@@ -4,7 +4,7 @@ import { type DefaultPermissionSetName } from "@fjall/generator";
4
4
  * construct. The discriminated union on `source` mirrors AWS ground truth:
5
5
  * the identity source of an Identity Center instance is neither readable nor
6
6
  * settable via any supported API, so the mode is user-declared and
7
- * authoritative. Design: aiDocs/designs/2026-07-04-org-user-management-design.md
7
+ * authoritative. Design: the 2026-07-04 organisation user-management design.
8
8
  *
9
9
  * The source vocabulary is canonical in @fjall/generator
10
10
  * (organisationSchemas.ts) — re-exported here so existing consumers keep
@@ -5,7 +5,7 @@ import { stripAndCamelCase } from "../../utils/stripAndCamelCase.js";
5
5
  * construct. The discriminated union on `source` mirrors AWS ground truth:
6
6
  * the identity source of an Identity Center instance is neither readable nor
7
7
  * settable via any supported API, so the mode is user-declared and
8
- * authoritative. Design: aiDocs/designs/2026-07-04-org-user-management-design.md
8
+ * authoritative. Design: the 2026-07-04 organisation user-management design.
9
9
  *
10
10
  * The source vocabulary is canonical in @fjall/generator
11
11
  * (organisationSchemas.ts) — re-exported here so existing consumers keep
@@ -140,8 +140,7 @@ function buildFoundationGuardrails(allowedRegions) {
140
140
  // assume-root sessions) are deliberately NOT carved out — recovery on
141
141
  // this tier is a break-glass SCP detach, not a standing carve-out. Do
142
142
  // not add them without revisiting the decision:
143
- // aiDocs/troubleshooting/centralised-root-access-break-glass-runbook.md
144
- // § Scenario 3.
143
+ // the centralised root-access break-glass runbook, § Scenario 3.
145
144
  NotAction: [
146
145
  "s3:GetBucketPolicy",
147
146
  "s3:PutBucketPolicy",
@@ -347,9 +346,19 @@ function buildCostControls() {
347
346
  "rds:ModifyDBCluster"
348
347
  ],
349
348
  Resource: "*",
349
+ // A negated operator over an ABSENT key evaluates true, so without
350
+ // the Null guard this statement denies every RDS request that names
351
+ // no instance class: a class-less ModifyDBInstance (the destroy
352
+ // pre-flight's DeletionProtection flip, a retention or parameter-group
353
+ // change), and Aurora's CreateDBCluster/ModifyDBCluster, where the
354
+ // class lives on the instances. The guard limits the deny to requests
355
+ // that carry a class; the instances still gate Aurora, whose Fjall
356
+ // construct is Serverless v2 only (`db.serverless`).
357
+ // https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_elements_condition_operators.html
350
358
  Condition: {
359
+ Null: { "rds:DatabaseClass": "false" },
351
360
  StringNotLike: {
352
- "rds:DatabaseClass": ["db.t3.*", "db.t4g.*"]
361
+ "rds:DatabaseClass": ["db.t3.*", "db.t4g.*", "db.serverless"]
353
362
  }
354
363
  }
355
364
  }
@@ -18117,3 +18117,129 @@ exports.handler = async (event) => {
18117
18117
  Data: { CaCertSha256: caCertSha256 }
18118
18118
  };
18119
18119
  };
18120
+ /*! Bundled license information:
18121
+
18122
+ reflect-metadata/Reflect.js:
18123
+ (*! *****************************************************************************
18124
+ Copyright (C) Microsoft. All rights reserved.
18125
+ Licensed under the Apache License, Version 2.0 (the "License"); you may not use
18126
+ this file except in compliance with the License. You may obtain a copy of the
18127
+ License at http://www.apache.org/licenses/LICENSE-2.0
18128
+
18129
+ THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18130
+ KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
18131
+ WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
18132
+ MERCHANTABLITY OR NON-INFRINGEMENT.
18133
+
18134
+ See the Apache Version 2.0 License for specific language governing permissions
18135
+ and limitations under the License.
18136
+ ***************************************************************************** *)
18137
+
18138
+ pvtsutils/build/index.js:
18139
+ (*!
18140
+ * MIT License
18141
+ *
18142
+ * Copyright (c) 2017-2024 Peculiar Ventures, LLC
18143
+ *
18144
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
18145
+ * of this software and associated documentation files (the "Software"), to deal
18146
+ * in the Software without restriction, including without limitation the rights
18147
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
18148
+ * copies of the Software, and to permit persons to whom the Software is
18149
+ * furnished to do so, subject to the following conditions:
18150
+ *
18151
+ * The above copyright notice and this permission notice shall be included in all
18152
+ * copies or substantial portions of the Software.
18153
+ *
18154
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18155
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18156
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18157
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18158
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18159
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
18160
+ * SOFTWARE.
18161
+ *
18162
+ *)
18163
+
18164
+ pvutils/build/utils.js:
18165
+ (*!
18166
+ Copyright (c) Peculiar Ventures, LLC
18167
+ *)
18168
+
18169
+ asn1js/build/index.js:
18170
+ (*!
18171
+ * Copyright (c) 2014, GMO GlobalSign
18172
+ * Copyright (c) 2015-2022, Peculiar Ventures
18173
+ * All rights reserved.
18174
+ *
18175
+ * Author 2014-2019, Yury Strozhevsky
18176
+ *
18177
+ * Redistribution and use in source and binary forms, with or without modification,
18178
+ * are permitted provided that the following conditions are met:
18179
+ *
18180
+ * * Redistributions of source code must retain the above copyright notice, this
18181
+ * list of conditions and the following disclaimer.
18182
+ *
18183
+ * * Redistributions in binary form must reproduce the above copyright notice, this
18184
+ * list of conditions and the following disclaimer in the documentation and/or
18185
+ * other materials provided with the distribution.
18186
+ *
18187
+ * * Neither the name of the copyright holder nor the names of its
18188
+ * contributors may be used to endorse or promote products derived from
18189
+ * this software without specific prior written permission.
18190
+ *
18191
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
18192
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18193
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18194
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
18195
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
18196
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
18197
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
18198
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
18199
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
18200
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
18201
+ *
18202
+ *)
18203
+
18204
+ tslib/tslib.es6.js:
18205
+ (*! *****************************************************************************
18206
+ Copyright (c) Microsoft Corporation.
18207
+
18208
+ Permission to use, copy, modify, and/or distribute this software for any
18209
+ purpose with or without fee is hereby granted.
18210
+
18211
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
18212
+ REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
18213
+ AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
18214
+ INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
18215
+ LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
18216
+ OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
18217
+ PERFORMANCE OF THIS SOFTWARE.
18218
+ ***************************************************************************** *)
18219
+
18220
+ @peculiar/x509/build/x509.cjs.js:
18221
+ (*!
18222
+ * MIT License
18223
+ *
18224
+ * Copyright (c) Peculiar Ventures. All rights reserved.
18225
+ *
18226
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
18227
+ * of this software and associated documentation files (the "Software"), to deal
18228
+ * in the Software without restriction, including without limitation the rights
18229
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
18230
+ * copies of the Software, and to permit persons to whom the Software is
18231
+ * furnished to do so, subject to the following conditions:
18232
+ *
18233
+ * The above copyright notice and this permission notice shall be included in all
18234
+ * copies or substantial portions of the Software.
18235
+ *
18236
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18237
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18238
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18239
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18240
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18241
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
18242
+ * SOFTWARE.
18243
+ *
18244
+ *)
18245
+ */
@@ -11,7 +11,8 @@ export function createBastion(networkStack, appName, stackPrefix, vpc, config) {
11
11
  serviceName: `${stackPrefix}Bastion`,
12
12
  instanceType,
13
13
  vpc,
14
- enableSSH: false,
14
+ // Session Manager only: no sshAccess, so no key pair, no port-22 ingress
15
+ // and private subnets.
15
16
  minCapacity: 1,
16
17
  maxCapacity: 1
17
18
  });
@@ -14,7 +14,7 @@ export type BuildkiteConstructProps = BuildkitePropsInput & {
14
14
  * Self-hosted Buildkite agent fleet on the pinned Elastic CI Stack AMIs —
15
15
  * scale-to-zero ASG + buildkite-agent-scaler, no secret ever transiting
16
16
  * synth, allowlist-only instance IAM. Design:
17
- * `aiDocs/designs/2026-07-18-buildkite-selfhosted-agents.md`.
17
+ * the 2026-07-18 Buildkite self-hosted agents design.
18
18
  *
19
19
  * A Construct, not a Stack: instantiate via `App.addBuildkite(props)`, which
20
20
  * scopes it under the default compute stack so deploy-core's fixed
@@ -21,7 +21,7 @@ import { buildBuildkiteUserData } from "./userData.js";
21
21
  * Self-hosted Buildkite agent fleet on the pinned Elastic CI Stack AMIs —
22
22
  * scale-to-zero ASG + buildkite-agent-scaler, no secret ever transiting
23
23
  * synth, allowlist-only instance IAM. Design:
24
- * `aiDocs/designs/2026-07-18-buildkite-selfhosted-agents.md`.
24
+ * the 2026-07-18 Buildkite self-hosted agents design.
25
25
  *
26
26
  * A Construct, not a Stack: instantiate via `App.addBuildkite(props)`, which
27
27
  * scopes it under the default compute stack so deploy-core's fixed
@@ -5,8 +5,8 @@ import type { BuildkitePropsInput } from "./schema.js";
5
5
  /**
6
6
  * Thunk returned by `BuildkiteFactory.build` — materialised only by
7
7
  * `App.addBuildkite`, which owns stack placement (the fleet has exactly one
8
- * legal home, `${stackPrefix}Compute`; design
9
- * `aiDocs/designs/2026-07-18-fjall-add-buildkite.md` § D2).
8
+ * legal home, `${stackPrefix}Compute`; design:
9
+ * the 2026-07-18 `fjall add buildkite` design, § D2).
10
10
  */
11
11
  export type BuildkiteFactoryFn = (app: App, scope: Construct) => Buildkite;
12
12
  /**
@@ -4,7 +4,7 @@
4
4
  * release. AMI ids, the elastic-stack version, the scaler version and the
5
5
  * secrets-plugin floor MUST only change together, regenerated from the
6
6
  * upstream template for the new version (runbook:
7
- * `aiDocs/runbooks/buildkite-stack-bump-runbook.md`).
7
+ * the Buildkite stack-bump runbook).
8
8
  *
9
9
  * AMI ids come from the `Mappings.AWSRegion2AMI` block of
10
10
  * `https://s3.amazonaws.com/buildkite-aws-stack/<version>/aws-stack.yml` and
@@ -18,7 +18,7 @@
18
18
  * floats the AMI underneath a pinned stack version, needs lookup credentials
19
19
  * at synth time (unavailable in worker synth), and makes synth
20
20
  * non-deterministic. Design:
21
- * `aiDocs/designs/2026-07-18-buildkite-selfhosted-agents.md` § D2.
21
+ * the 2026-07-18 Buildkite self-hosted agents design, § D2.
22
22
  */
23
23
  export declare const BUILDKITE_AMI_OWNER_ACCOUNT_ID = "172840064832";
24
24
  export declare const BUILDKITE_CPU_ARCHITECTURES: readonly ["arm64", "amd64"];
@@ -4,7 +4,7 @@
4
4
  * release. AMI ids, the elastic-stack version, the scaler version and the
5
5
  * secrets-plugin floor MUST only change together, regenerated from the
6
6
  * upstream template for the new version (runbook:
7
- * `aiDocs/runbooks/buildkite-stack-bump-runbook.md`).
7
+ * the Buildkite stack-bump runbook).
8
8
  *
9
9
  * AMI ids come from the `Mappings.AWSRegion2AMI` block of
10
10
  * `https://s3.amazonaws.com/buildkite-aws-stack/<version>/aws-stack.yml` and
@@ -18,7 +18,7 @@
18
18
  * floats the AMI underneath a pinned stack version, needs lookup credentials
19
19
  * at synth time (unavailable in worker synth), and makes synth
20
20
  * non-deterministic. Design:
21
- * `aiDocs/designs/2026-07-18-buildkite-selfhosted-agents.md` § D2.
21
+ * the 2026-07-18 Buildkite self-hosted agents design, § D2.
22
22
  */
23
23
  export const BUILDKITE_AMI_OWNER_ACCOUNT_ID = "172840064832";
24
24
  export const BUILDKITE_CPU_ARCHITECTURES = ["arm64", "amd64"];
@@ -2,7 +2,7 @@
2
2
  * Buildkite props schema — re-export barrel. The canonical declaration lives
3
3
  * in `@fjall/generator` (`src/schemas/buildkiteSchemas.ts`) so construct
4
4
  * validation, the codemod fragment, and the LLM JSON schema all consume one
5
- * source (design `aiDocs/designs/2026-07-18-fjall-add-buildkite.md` § D4).
5
+ * source (design: the 2026-07-18 `fjall add buildkite` design, § D4).
6
6
  * Do NOT redeclare here — the cross-package reference-identity test pins it.
7
7
  */
8
8
  export { BuildkitePropsObjectSchema, BuildkitePropsSchema, validateBuildkiteProps, type BuildkiteProps, type BuildkitePropsInput, type BuildkitePropsObject } from "@fjall/generator";
@@ -2,7 +2,7 @@
2
2
  * Buildkite props schema — re-export barrel. The canonical declaration lives
3
3
  * in `@fjall/generator` (`src/schemas/buildkiteSchemas.ts`) so construct
4
4
  * validation, the codemod fragment, and the LLM JSON schema all consume one
5
- * source (design `aiDocs/designs/2026-07-18-fjall-add-buildkite.md` § D4).
5
+ * source (design: the 2026-07-18 `fjall add buildkite` design, § D4).
6
6
  * Do NOT redeclare here — the cross-package reference-identity test pins it.
7
7
  */
8
8
  export { BuildkitePropsObjectSchema, BuildkitePropsSchema, validateBuildkiteProps } from "@fjall/generator";
@@ -29,7 +29,7 @@ export interface BuildkiteUserDataContext {
29
29
  * vendored template in both directions: a key we emit that upstream does not
30
30
  * pass is silently ignored; a key upstream passes that we omit is the
31
31
  * boot-loop. Bumping the pins regenerates the fixture
32
- * (runbook: `aiDocs/runbooks/buildkite-stack-bump-runbook.md`).
32
+ * (runbook: the Buildkite stack-bump runbook).
33
33
  */
34
34
  export declare const BUILDKITE_MOUNT_PART_ENV_KEYS: readonly ["BUILDKITE_ENABLE_INSTANCE_STORAGE", "BUILDKITE_MOUNT_TMPFS_AT_TMP"];
35
35
  export declare const BUILDKITE_DOCKER_PART_ENV_KEYS: readonly ["DOCKER_USERNS_REMAP", "DOCKER_EXPERIMENTAL", "DOCKER_PRUNE_UNTIL", "ENABLE_PRE_EXIT_DISK_CLEANUP", "DOCKER_BUILDER_PRUNE_ENABLED", "DOCKER_NETWORKING_PROTOCOL", "DOCKER_IPV4_ADDRESS_POOL_1", "DOCKER_IPV4_ADDRESS_POOL_2", "DOCKER_IPV6_ADDRESS_POOL", "DOCKER_FIXED_CIDR_V4", "DOCKER_FIXED_CIDR_V6", "BUILDKITE_ENABLE_INSTANCE_STORAGE"];
@@ -10,7 +10,7 @@ import { BUILDKITE_STACK_PINS } from "./pins.js";
10
10
  * vendored template in both directions: a key we emit that upstream does not
11
11
  * pass is silently ignored; a key upstream passes that we omit is the
12
12
  * boot-loop. Bumping the pins regenerates the fixture
13
- * (runbook: `aiDocs/runbooks/buildkite-stack-bump-runbook.md`).
13
+ * (runbook: the Buildkite stack-bump runbook).
14
14
  */
15
15
  export const BUILDKITE_MOUNT_PART_ENV_KEYS = [
16
16
  "BUILDKITE_ENABLE_INSTANCE_STORAGE",
@@ -2,7 +2,7 @@
2
2
  * Self-hosted Buildkite agent fleet — barrel for the `buildkite/` modules.
3
3
  * The pre-2026-07 `Buildkite extends Stack` implementation is replaced by a
4
4
  * Construct instantiated via `App.addBuildkite()`; design:
5
- * `aiDocs/designs/2026-07-18-buildkite-selfhosted-agents.md`.
5
+ * the 2026-07-18 Buildkite self-hosted agents design.
6
6
  */
7
7
  export { AGENT_TOKEN_SECRET_NAME, BUILDKITE_SECRETS_CLUSTER, Buildkite, FJALL_API_KEY_SECRET_NAME, FJALL_ENV_HOOK_OBJECT_KEY, buildAgentSecretsEnvHookScript, type AgentSecretsEnvHookOptions, type BuildkiteConstructProps } from "./buildkite/buildkite.js";
8
8
  export { BUILDKITE_AMI_OWNER_ACCOUNT_ID, BUILDKITE_CPU_ARCHITECTURES, BUILDKITE_STACK_PINS, resolvePinnedAmiId, resolveScalerSarApplicationArn, type BuildkiteCpuArchitecture } from "./buildkite/pins.js";
@@ -2,7 +2,7 @@
2
2
  * Self-hosted Buildkite agent fleet — barrel for the `buildkite/` modules.
3
3
  * The pre-2026-07 `Buildkite extends Stack` implementation is replaced by a
4
4
  * Construct instantiated via `App.addBuildkite()`; design:
5
- * `aiDocs/designs/2026-07-18-buildkite-selfhosted-agents.md`.
5
+ * the 2026-07-18 Buildkite self-hosted agents design.
6
6
  */
7
7
  export { AGENT_TOKEN_SECRET_NAME, BUILDKITE_SECRETS_CLUSTER, Buildkite, FJALL_API_KEY_SECRET_NAME, FJALL_ENV_HOOK_OBJECT_KEY, buildAgentSecretsEnvHookScript } from "./buildkite/buildkite.js";
8
8
  export { BUILDKITE_AMI_OWNER_ACCOUNT_ID, BUILDKITE_CPU_ARCHITECTURES, BUILDKITE_STACK_PINS, resolvePinnedAmiId, resolveScalerSarApplicationArn } from "./buildkite/pins.js";
@@ -87,7 +87,10 @@ interface BaseCdnProps {
87
87
  * `cloudFrontLogging` column — enterprise logs to the stack's shared,
88
88
  * lifecycle-expired access-log bucket under `cloudfront/<id>/`. Set either
89
89
  * knob to take over: `enableLogging: false` opts out at any tier,
90
- * `logBucket` delivers to a bucket of your own.
90
+ * `logBucket` delivers to a bucket of your own. Standard logging cannot
91
+ * deliver into an opt-in region, so a tier- or explicitly-enabled CDN whose
92
+ * delivery region is opt-in is refused at synth with those two cures — see
93
+ * {@link CloudFrontDistributionProps.enableLogging}.
91
94
  */
92
95
  enableLogging?: boolean;
93
96
  logBucket?: IBucket;
@@ -16,7 +16,6 @@ import { S3Bucket } from "../../resources/aws/storage/s3.js";
16
16
  import { Secret } from "../../resources/aws/secrets/secret.js";
17
17
  import { LogGroup } from "../../resources/aws/logging/logGroup.js";
18
18
  import { StackEncryptionKey } from "../../resources/aws/secrets/stackEncryptionKey.js";
19
- import { tierDefault } from "../../utils/backupTierMapping.js";
20
19
  import { createClickHouseSecurityGroup } from "../../resources/aws/database/clickhouseSecurityGroup.js";
21
20
  import { buildMaintenancePreamble, maintenanceOkMarker, maintenanceRun } from "../../resources/aws/database/clickhouseMaintenanceScript.js";
22
21
  import { buildClickHouseEntrypointWrapper, buildClickHouseUserData, generateUsersConfigXml } from "../../resources/aws/database/clickhouseUserData.js";
@@ -381,7 +380,10 @@ export class ClickHouseDatabase extends Construct {
381
380
  const coldTierBucket = coldTierEnabled
382
381
  ? (props.coldTierBucket ??
383
382
  new S3Bucket(this, "ClickHouseColdTierBucket", {
384
- versioned: false
383
+ // Tiered table parts, re-tiered from hot storage — versioning would
384
+ // only bill for churn; the exemption stamp tells posture so.
385
+ versioned: false,
386
+ versioningExemption: "cold-tier"
385
387
  }))
386
388
  : undefined;
387
389
  this.#backupBucket = backupBucket;
@@ -390,9 +392,8 @@ export class ClickHouseDatabase extends Construct {
390
392
  // The maintenance-task log groups sit beside the compute's own container
391
393
  // and exec-audit groups and follow the same tier: the stack's shared key
392
394
  // at enterprise, nothing otherwise.
393
- const taskLogGroupEncryption = tierDefault(props.appBackupTier, "customerManagedKeys")
394
- ? { encryptionKey: StackEncryptionKey.of(this).forLogGroups() }
395
- : {};
395
+ const stackKey = StackEncryptionKey.forTier(this, props.appBackupTier);
396
+ const taskLogGroupEncryption = stackKey === undefined ? {} : { encryptionKey: stackKey.forLogGroups() };
396
397
  const backupTaskLogGroup = backupEnabled
397
398
  ? new LogGroup(this, "ClickHouseBackupTaskLogGroup", {
398
399
  retention: RetentionDays.TWO_WEEKS,
@@ -17,7 +17,7 @@ export interface ClickHouseSelfSignedTls {
17
17
  * supplied the construct surfaces it as a CFN env-var token so consumers
18
18
  * can wire it into containers for rotation-triggers-task-replacement
19
19
  * parity with self-signed mode. Omit it and rotation requires manual task
20
- * restart (documented in `aiDocs/patterns/clickhouse-tls-pattern.md § "Imported mode"`).
20
+ * restart (documented in the ClickHouse TLS pattern, § "Imported mode").
21
21
  */
22
22
  export interface ClickHouseImportedTls {
23
23
  readonly mode: "imported";
@@ -5,8 +5,8 @@ import { type ComputeType, type IEcsCompute, type ILambdaCompute, type IEc2Compu
5
5
  import type App from "../../app.js";
6
6
  import { EcsCompute, type EcsComputeProps, type EcsServiceConfig, type ServiceLogAlarm, type EcsContainerConfig, type EcsScalingConfig, type EcsClusterConfig, type EcsRoutingConfig, type EcsCapacityProviderConfig, type ContainerDependency, type EcsMigrationsConfig, ECS_CAPACITY_PROVIDER_CONFIG, getEcsCapacityProviderConfig, ScalingType, type EcsCapacityProvider, type Ec2CapacityConfig, validateEcsProps, buildContainerConfigs, expandMigrationsSugar, type ResolvedScalingConfig, resolveScalingConfig } from "./computeEcs.js";
7
7
  import { LambdaCompute, type LambdaComputeProps, type ContainerLambdaProps, type CodeLambdaProps, type FunctionUrlConfig, type ResolvedLambdaDeployment, resolveLambdaDeployment, lambdaImageKey, Architecture, HttpMethod, InvokeMode, type FunctionUrlCorsOptions } from "./computeLambda.js";
8
- import { Ec2Compute, type Ec2ComputeProps, type SshConfig } from "./computeEc2.js";
9
- export { EcsCompute, type EcsComputeProps, type EcsServiceConfig, type ServiceLogAlarm, type EcsContainerConfig, type EcsScalingConfig, type EcsClusterConfig, type EcsRoutingConfig, type EcsCapacityProviderConfig, ECS_CAPACITY_PROVIDER_CONFIG, getEcsCapacityProviderConfig, ScalingType, type EcsCapacityProvider, type Ec2CapacityConfig, type ContainerDependency, type EcsMigrationsConfig, validateEcsProps, buildContainerConfigs, expandMigrationsSugar, type ResolvedScalingConfig, resolveScalingConfig, LambdaCompute, type LambdaComputeProps, type ContainerLambdaProps, type CodeLambdaProps, type FunctionUrlConfig, type ResolvedLambdaDeployment, resolveLambdaDeployment, lambdaImageKey, Architecture, HttpMethod, InvokeMode, type FunctionUrlCorsOptions, Ec2Compute, type Ec2ComputeProps, type SshConfig };
8
+ import { Ec2Compute, type Ec2ComputeProps } from "./computeEc2.js";
9
+ export { EcsCompute, type EcsComputeProps, type EcsServiceConfig, type ServiceLogAlarm, type EcsContainerConfig, type EcsScalingConfig, type EcsClusterConfig, type EcsRoutingConfig, type EcsCapacityProviderConfig, ECS_CAPACITY_PROVIDER_CONFIG, getEcsCapacityProviderConfig, ScalingType, type EcsCapacityProvider, type Ec2CapacityConfig, type ContainerDependency, type EcsMigrationsConfig, validateEcsProps, buildContainerConfigs, expandMigrationsSugar, type ResolvedScalingConfig, resolveScalingConfig, LambdaCompute, type LambdaComputeProps, type ContainerLambdaProps, type CodeLambdaProps, type FunctionUrlConfig, type ResolvedLambdaDeployment, resolveLambdaDeployment, lambdaImageKey, Architecture, HttpMethod, InvokeMode, type FunctionUrlCorsOptions, Ec2Compute, type Ec2ComputeProps };
10
10
  export type { ComputeType } from "./interfaces/compute.js";
11
11
  /**
12
12
  * Configuration defaults for each compute type.
@@ -3,31 +3,18 @@ import { type IAutoScalingGroup } from "aws-cdk-lib/aws-autoscaling";
3
3
  import { Construct } from "constructs";
4
4
  import { type IEc2Compute } from "./interfaces/compute.js";
5
5
  import { Ec2Instance } from "../../resources/aws/compute/ec2.js";
6
- /**
7
- * SSH access configuration for EC2 instances.
8
- * - Omit: disabled (default)
9
- * - `{}`: enabled with auto-generated key
10
- * - `{ keyName: "my-key" }`: enabled with existing key
11
- * - `false`: explicitly disabled
12
- */
13
- export interface SshConfig {
14
- /** SSH key pair name */
15
- keyName?: string;
16
- /** Allowed CIDR blocks for SSH access */
17
- allowedCidrs?: string[];
18
- }
6
+ import { type Ec2SshAccessProps } from "../../resources/aws/compute/ec2SshAccess.js";
19
7
  export interface Ec2ComputeProps {
20
8
  type: "ec2";
21
9
  vpc?: IVpc;
22
10
  /** EC2 instance type. Default: "t4g.micro" */
23
11
  instanceType?: string;
24
12
  /**
25
- * SSH access configuration.
26
- * - Omit: disabled (default)
27
- * - `{}`: enabled with defaults
28
- * - `false`: explicitly disabled
13
+ * SSH access: who may reach port 22 (`allowedIpCidrs`), which key pair
14
+ * (`keyName`, or omit to mint one) and whether the instances are public
15
+ * (`public`). Omit for Session Manager-only access, the default.
29
16
  */
30
- ssh?: SshConfig | false;
17
+ sshAccess?: Ec2SshAccessProps;
31
18
  /** User data script */
32
19
  userData?: UserData;
33
20
  /** Machine image (AMI) */
@@ -1,5 +1,7 @@
1
1
  import { Construct } from "constructs";
2
+ import { ec2ServiceName } from "@fjall/util";
2
3
  import { Ec2Instance } from "../../resources/aws/compute/ec2.js";
4
+ import { checkRetiredSshProp } from "../../resources/aws/compute/ec2SshAccess.js";
3
5
  import { COMPUTE_DEFAULTS } from "./compute.js";
4
6
  /**
5
7
  * EC2 compute wrapper implementing IEc2Compute.
@@ -11,11 +13,15 @@ export class Ec2Compute extends Construct {
11
13
  ec2Instance;
12
14
  constructor(scope, id, props) {
13
15
  super(scope, id);
14
- const enableSSH = props.ssh !== undefined && props.ssh !== false;
16
+ // The retired `ssh` prop read only its own presence (`keyName` and
17
+ // `allowedCidrs` were declared and never forwarded); a caller still
18
+ // passing it gets the translation, or a warning when it asked for the
19
+ // default.
20
+ checkRetiredSshProp(this, "Ec2Compute", props);
15
21
  const ec2Props = {
16
- serviceName: `${id}Service`,
22
+ serviceName: ec2ServiceName(id),
17
23
  instanceType: props.instanceType || COMPUTE_DEFAULTS.EC2.INSTANCE_TYPE,
18
- enableSSH,
24
+ ...(props.sshAccess !== undefined && { sshAccess: props.sshAccess }),
19
25
  userData: props.userData,
20
26
  machineImage: props.machineImage,
21
27
  minCapacity: props.minCapacity || COMPUTE_DEFAULTS.EC2.MIN_CAPACITY,
@@ -1,6 +1,6 @@
1
1
  import { type ICluster, type IBaseService, type TaskDefinition, type FargateTaskDefinition } from "aws-cdk-lib/aws-ecs";
2
2
  import { type Connections, type ISecurityGroup } from "aws-cdk-lib/aws-ec2";
3
- import { type IGrantable, type IRole, Grant } from "aws-cdk-lib/aws-iam";
3
+ import { type Grant, type IGrantable, type IRole } from "aws-cdk-lib/aws-iam";
4
4
  import { type IApplicationLoadBalancer, type ApplicationListener } from "aws-cdk-lib/aws-elasticloadbalancingv2";
5
5
  import { Construct } from "constructs";
6
6
  import { type IEcsCompute } from "./interfaces/compute.js";
@@ -264,9 +264,9 @@ export declare class EcsCompute extends Construct implements IEcsCompute {
264
264
  */
265
265
  getEcsCluster(): EcsCluster;
266
266
  /**
267
- * Grants ecs:ExecuteCommand permission for ECS services.
268
- * Uses wildcard resource because ecs:ExecuteCommand targets task ARNs
269
- * which are not known until runtime (tasks are ephemeral).
267
+ * Grant what an ECS Exec caller needs: `ecs:ExecuteCommand` on this
268
+ * cluster's tasks and, at a tier that encrypts the session, the
269
+ * `kms:GenerateDataKey` on the stack key the caller must hold.
270
270
  */
271
271
  grantExecuteCommand(grantee: IGrantable): Grant;
272
272
  /**
@@ -1,6 +1,6 @@
1
1
  import { AwsLogDriver, ContainerImage, DeploymentLifecycleStage, Secret as EcsSecret } from "aws-cdk-lib/aws-ecs";
2
2
  import { Peer, Port, SubnetType } from "aws-cdk-lib/aws-ec2";
3
- import { Effect, Grant, PolicyStatement, ServicePrincipal } from "aws-cdk-lib/aws-iam";
3
+ import { Effect, PolicyStatement, ServicePrincipal } from "aws-cdk-lib/aws-iam";
4
4
  import { Annotations, Aws, Stack, Token } from "aws-cdk-lib";
5
5
  import { RetentionDays } from "aws-cdk-lib/aws-logs";
6
6
  import { StringParameter } from "aws-cdk-lib/aws-ssm";
@@ -1688,16 +1688,12 @@ export class EcsCompute extends Construct {
1688
1688
  return this.ecsCluster;
1689
1689
  }
1690
1690
  /**
1691
- * Grants ecs:ExecuteCommand permission for ECS services.
1692
- * Uses wildcard resource because ecs:ExecuteCommand targets task ARNs
1693
- * which are not known until runtime (tasks are ephemeral).
1691
+ * Grant what an ECS Exec caller needs: `ecs:ExecuteCommand` on this
1692
+ * cluster's tasks and, at a tier that encrypts the session, the
1693
+ * `kms:GenerateDataKey` on the stack key the caller must hold.
1694
1694
  */
1695
1695
  grantExecuteCommand(grantee) {
1696
- return Grant.addToPrincipal({
1697
- grantee,
1698
- actions: ["ecs:ExecuteCommand"],
1699
- resourceArns: ["*"]
1700
- });
1696
+ return this.ecsCluster.grantExecuteCommand(grantee);
1701
1697
  }
1702
1698
  /**
1703
1699
  * Get the EC2 instance role for the cluster's underlying ASG. EC2-mode