@fjall/components-infrastructure 0.96.0 → 0.99.1

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 (209) hide show
  1. package/dist/lib/app.d.ts +68 -1
  2. package/dist/lib/app.js +113 -4
  3. package/dist/lib/config/aws/__t17fixture.d.ts +1 -0
  4. package/dist/lib/config/aws/__t17fixture.js +3 -0
  5. package/dist/lib/config/aws/__t17fixtureType.d.ts +2 -0
  6. package/dist/lib/config/aws/__t17fixtureType.js +1 -0
  7. package/dist/lib/config/aws/alarmTopic.js +8 -4
  8. package/dist/lib/config/aws/cloudTrail.js +1 -1
  9. package/dist/lib/config/aws/disasterRecovery.js +11 -16
  10. package/dist/lib/config/aws/ecrDefaultImage.d.ts +0 -1
  11. package/dist/lib/config/aws/ecrDefaultImage.js +13 -23
  12. package/dist/lib/config/aws/identityCenter.d.ts +10 -3
  13. package/dist/lib/config/aws/identityCenter.js +101 -37
  14. package/dist/lib/config/aws/identityCenterGroupMembership.js +8 -2
  15. package/dist/lib/config/aws/identityCenterMembership.d.ts +11 -0
  16. package/dist/lib/config/aws/identityCenterMembership.js +61 -0
  17. package/dist/lib/config/aws/index.d.ts +1 -1
  18. package/dist/lib/config/aws/index.js +1 -1
  19. package/dist/lib/config/aws/ipam.js +6 -11
  20. package/dist/lib/config/aws/oidcConnector.js +5 -1
  21. package/dist/lib/config/aws/scpPreset.js +4 -1
  22. package/dist/lib/patterns/aws/_eslint_test_tmp/leak.d.ts +1 -0
  23. package/dist/lib/patterns/aws/_eslint_test_tmp/leak.js +4 -0
  24. package/dist/lib/patterns/aws/account.js +2 -4
  25. package/dist/lib/patterns/aws/apexDomainPattern.js +10 -10
  26. package/dist/lib/patterns/aws/bastionFactory.d.ts +10 -0
  27. package/dist/lib/patterns/aws/bastionFactory.js +29 -0
  28. package/dist/lib/patterns/aws/buildkite.d.ts +2 -2
  29. package/dist/lib/patterns/aws/buildkite.js +51 -97
  30. package/dist/lib/patterns/aws/cdn.js +1 -1
  31. package/dist/lib/patterns/aws/clickhouseDatabase.d.ts +172 -0
  32. package/dist/lib/patterns/aws/clickhouseDatabase.js +600 -0
  33. package/dist/lib/patterns/aws/compute.d.ts +4 -6
  34. package/dist/lib/patterns/aws/compute.js +7 -13
  35. package/dist/lib/patterns/aws/computeEcs.d.ts +93 -5
  36. package/dist/lib/patterns/aws/computeEcs.js +867 -37
  37. package/dist/lib/patterns/aws/computeEcsTypes.d.ts +528 -25
  38. package/dist/lib/patterns/aws/computeEcsTypes.js +10 -0
  39. package/dist/lib/patterns/aws/computeLambda.d.ts +0 -5
  40. package/dist/lib/patterns/aws/computeLambda.js +1 -2
  41. package/dist/lib/patterns/aws/database.d.ts +50 -8
  42. package/dist/lib/patterns/aws/database.js +183 -27
  43. package/dist/lib/patterns/aws/domain.js +6 -4
  44. package/dist/lib/patterns/aws/index.d.ts +1 -0
  45. package/dist/lib/patterns/aws/index.js +1 -0
  46. package/dist/lib/patterns/aws/interfaces/compute.d.ts +7 -1
  47. package/dist/lib/patterns/aws/interfaces/database.d.ts +187 -8
  48. package/dist/lib/patterns/aws/interfaces/database.js +17 -3
  49. package/dist/lib/patterns/aws/interfaces/index.d.ts +2 -1
  50. package/dist/lib/patterns/aws/interfaces/index.js +3 -1
  51. package/dist/lib/patterns/aws/interfaces/messaging.d.ts +7 -0
  52. package/dist/lib/patterns/aws/interfaces/migrationContributor.d.ts +47 -0
  53. package/dist/lib/patterns/aws/interfaces/migrationContributor.js +9 -0
  54. package/dist/lib/patterns/aws/messaging.d.ts +66 -10
  55. package/dist/lib/patterns/aws/messaging.js +115 -20
  56. package/dist/lib/patterns/aws/network.js +16 -7
  57. package/dist/lib/patterns/aws/organisation.d.ts +4 -0
  58. package/dist/lib/patterns/aws/organisation.js +22 -4
  59. package/dist/lib/patterns/aws/storage.d.ts +1 -2
  60. package/dist/lib/patterns/aws/storage.js +3 -2
  61. package/dist/lib/patterns/aws/vpcPeer.js +3 -1
  62. package/dist/lib/resources/aws/analytics/clickhouse.js +18 -9
  63. package/dist/lib/resources/aws/analytics/clickhouseAlarms.d.ts +24 -9
  64. package/dist/lib/resources/aws/analytics/clickhouseAlarms.js +61 -10
  65. package/dist/lib/resources/aws/analytics/clickhouseConstants.d.ts +3 -3
  66. package/dist/lib/resources/aws/analytics/clickhouseConstants.js +3 -3
  67. package/dist/lib/resources/aws/analytics/clickhouseTypes.d.ts +7 -1
  68. package/dist/lib/resources/aws/analytics/clickhouseUserData.d.ts +1 -1
  69. package/dist/lib/resources/aws/analytics/clickhouseUserData.js +53 -3
  70. package/dist/lib/resources/aws/base/awsStack.js +4 -2
  71. package/dist/lib/resources/aws/compute/__tmp__/regression-shape.d.ts +2 -0
  72. package/dist/lib/resources/aws/compute/__tmp__/regression-shape.js +11 -0
  73. package/dist/lib/resources/aws/compute/asgInlineLifecycleHook.d.ts +52 -0
  74. package/dist/lib/resources/aws/compute/asgInlineLifecycleHook.js +60 -0
  75. package/dist/lib/resources/aws/compute/blockDeviceVolume.d.ts +8 -0
  76. package/dist/lib/resources/aws/compute/blockDeviceVolume.js +10 -0
  77. package/dist/lib/resources/aws/compute/ec2.d.ts +132 -12
  78. package/dist/lib/resources/aws/compute/ec2.js +163 -23
  79. package/dist/lib/resources/aws/compute/ec2GracefulTerminationHandler.d.ts +41 -0
  80. package/dist/lib/resources/aws/compute/ec2GracefulTerminationHandler.js +194 -0
  81. package/dist/lib/resources/aws/compute/ec2GracefulTerminationLambda.source.cjs +458 -0
  82. package/dist/lib/resources/aws/compute/ecs.d.ts +27 -1
  83. package/dist/lib/resources/aws/compute/ecs.js +42 -2
  84. package/dist/lib/resources/aws/compute/ecsConstants.d.ts +9 -0
  85. package/dist/lib/resources/aws/compute/ecsConstants.js +16 -0
  86. package/dist/lib/resources/aws/compute/ecsImages.js +32 -20
  87. package/dist/lib/resources/aws/compute/ecsLifecycleHookMigration.d.ts +96 -0
  88. package/dist/lib/resources/aws/compute/ecsLifecycleHookMigration.js +113 -0
  89. package/dist/lib/resources/aws/compute/ecsNetworking.d.ts +2 -1
  90. package/dist/lib/resources/aws/compute/ecsNetworking.js +18 -6
  91. package/dist/lib/resources/aws/compute/ecsServiceFactory.d.ts +13 -4
  92. package/dist/lib/resources/aws/compute/ecsServiceFactory.js +155 -33
  93. package/dist/lib/resources/aws/compute/ecsTaskDefinition.d.ts +31 -1
  94. package/dist/lib/resources/aws/compute/ecsTaskDefinition.js +102 -6
  95. package/dist/lib/resources/aws/compute/ecsTypes.d.ts +173 -13
  96. package/dist/lib/resources/aws/compute/ecsValidation.d.ts +9 -0
  97. package/dist/lib/resources/aws/compute/ecsValidation.js +63 -0
  98. package/dist/lib/resources/aws/compute/index.d.ts +2 -0
  99. package/dist/lib/resources/aws/compute/index.js +2 -0
  100. package/dist/lib/resources/aws/compute/lambda.d.ts +7 -13
  101. package/dist/lib/resources/aws/compute/lambda.js +30 -38
  102. package/dist/lib/resources/aws/compute/lifecycleHookLambda.source.cjs +192 -0
  103. package/dist/lib/resources/aws/compute/persistentDataVolume.d.ts +104 -0
  104. package/dist/lib/resources/aws/compute/persistentDataVolume.js +245 -0
  105. package/dist/lib/resources/aws/compute/persistentDataVolumeLambda.source.cjs +398 -0
  106. package/dist/lib/resources/aws/compute/samApplication.d.ts +15 -0
  107. package/dist/lib/resources/aws/compute/samApplication.js +27 -0
  108. package/dist/lib/resources/aws/database/clickhouseConstants.d.ts +159 -0
  109. package/dist/lib/resources/aws/database/clickhouseConstants.js +181 -0
  110. package/dist/lib/resources/aws/database/clickhouseSchemas.d.ts +71 -0
  111. package/dist/lib/resources/aws/database/clickhouseSchemas.js +157 -0
  112. package/dist/lib/resources/aws/database/clickhouseSecurityGroup.d.ts +14 -0
  113. package/dist/lib/resources/aws/database/clickhouseSecurityGroup.js +23 -0
  114. package/dist/lib/resources/aws/database/clickhouseUserData.d.ts +69 -0
  115. package/dist/lib/resources/aws/database/clickhouseUserData.js +371 -0
  116. package/dist/lib/resources/aws/database/clickhouseXmlRenderer.d.ts +56 -0
  117. package/dist/lib/resources/aws/database/clickhouseXmlRenderer.js +112 -0
  118. package/dist/lib/resources/aws/database/rdsAurora.d.ts +8 -1
  119. package/dist/lib/resources/aws/database/rdsAurora.js +42 -32
  120. package/dist/lib/resources/aws/database/rdsAuroraGlobal.d.ts +15 -2
  121. package/dist/lib/resources/aws/database/rdsAuroraGlobal.js +39 -43
  122. package/dist/lib/resources/aws/database/rdsDefaults.d.ts +6 -0
  123. package/dist/lib/resources/aws/database/rdsDefaults.js +7 -1
  124. package/dist/lib/resources/aws/database/rdsHelpers.d.ts +3 -3
  125. package/dist/lib/resources/aws/database/rdsHelpers.js +1 -0
  126. package/dist/lib/resources/aws/database/rdsInstance.d.ts +8 -1
  127. package/dist/lib/resources/aws/database/rdsInstance.js +51 -34
  128. package/dist/lib/resources/aws/database/rdsProxyOutput.d.ts +1 -1
  129. package/dist/lib/resources/aws/database/rdsProxyOutput.js +1 -1
  130. package/dist/lib/resources/aws/iam/delegationRole.js +1 -1
  131. package/dist/lib/resources/aws/iam/identityCenter/groupMembership.d.ts +9 -0
  132. package/dist/lib/resources/aws/iam/identityCenter/groupMembership.js +12 -0
  133. package/dist/lib/resources/aws/iam/identityCenter/index.d.ts +1 -0
  134. package/dist/lib/resources/aws/iam/identityCenter/index.js +1 -0
  135. package/dist/lib/resources/aws/iam/identityCenter/permissionSet.d.ts +1 -0
  136. package/dist/lib/resources/aws/iam/identityCenter/permissionSet.js +1 -0
  137. package/dist/lib/resources/aws/logging/logGroup.d.ts +0 -8
  138. package/dist/lib/resources/aws/logging/logGroup.js +0 -11
  139. package/dist/lib/resources/aws/messaging/defaultEventBus.d.ts +7 -0
  140. package/dist/lib/resources/aws/messaging/defaultEventBus.js +21 -0
  141. package/dist/lib/resources/aws/messaging/eventBridgeRule.d.ts +96 -0
  142. package/dist/lib/resources/aws/messaging/eventBridgeRule.js +110 -0
  143. package/dist/lib/resources/aws/messaging/eventTargets.d.ts +84 -0
  144. package/dist/lib/resources/aws/messaging/eventTargets.js +152 -0
  145. package/dist/lib/resources/aws/messaging/eventbridge.d.ts +25 -2
  146. package/dist/lib/resources/aws/messaging/eventbridge.js +22 -10
  147. package/dist/lib/resources/aws/messaging/index.d.ts +5 -0
  148. package/dist/lib/resources/aws/messaging/index.js +2 -0
  149. package/dist/lib/resources/aws/messaging/schedule.d.ts +118 -0
  150. package/dist/lib/resources/aws/messaging/schedule.js +64 -0
  151. package/dist/lib/resources/aws/messaging/sns.d.ts +2 -1
  152. package/dist/lib/resources/aws/messaging/sqs.d.ts +2 -1
  153. package/dist/lib/resources/aws/messaging/subscription.d.ts +112 -0
  154. package/dist/lib/resources/aws/messaging/subscription.js +67 -0
  155. package/dist/lib/resources/aws/messaging/utils.d.ts +6 -0
  156. package/dist/lib/resources/aws/messaging/utils.js +10 -0
  157. package/dist/lib/resources/aws/monitoring/clickhouseAlarms.d.ts +60 -0
  158. package/dist/lib/resources/aws/monitoring/clickhouseAlarms.js +139 -0
  159. package/dist/lib/resources/aws/monitoring/index.d.ts +2 -0
  160. package/dist/lib/resources/aws/monitoring/index.js +2 -0
  161. package/dist/lib/resources/aws/monitoring/scheduleAlarms.d.ts +47 -0
  162. package/dist/lib/resources/aws/monitoring/scheduleAlarms.js +106 -0
  163. package/dist/lib/resources/aws/networking/crossAccountDelegationRecord.js +6 -4
  164. package/dist/lib/resources/aws/networking/crossAccountReturnRoutes.js +17 -13
  165. package/dist/lib/resources/aws/networking/dnsRecord/dnsRecordBase.js +7 -5
  166. package/dist/lib/resources/aws/networking/domainCertificate.d.ts +2 -2
  167. package/dist/lib/resources/aws/networking/domainCertificate.js +6 -4
  168. package/dist/lib/resources/aws/networking/hostedZone.js +6 -5
  169. package/dist/lib/resources/aws/networking/serviceDiscovery.d.ts +96 -0
  170. package/dist/lib/resources/aws/networking/serviceDiscovery.js +96 -0
  171. package/dist/lib/resources/aws/networking/vpc.d.ts +4 -1
  172. package/dist/lib/resources/aws/networking/vpc.js +4 -1
  173. package/dist/lib/resources/aws/networking/vpcPeeringConnection.js +21 -3
  174. package/dist/lib/resources/aws/organisation/costAllocationTagActivator.d.ts +16 -5
  175. package/dist/lib/resources/aws/organisation/costAllocationTagActivator.js +17 -3
  176. package/dist/lib/resources/aws/organisation/index.d.ts +1 -1
  177. package/dist/lib/resources/aws/organisation/organisationPolicy.d.ts +2 -0
  178. package/dist/lib/resources/aws/organisation/organisationPolicy.js +3 -2
  179. package/dist/lib/resources/aws/secrets/secret.d.ts +7 -0
  180. package/dist/lib/resources/aws/secrets/secret.js +4 -3
  181. package/dist/lib/resources/aws/storage/bucketDeployment.d.ts +16 -0
  182. package/dist/lib/resources/aws/storage/bucketDeployment.js +17 -0
  183. package/dist/lib/resources/aws/storage/ecr.js +5 -5
  184. package/dist/lib/resources/aws/storage/index.d.ts +1 -0
  185. package/dist/lib/resources/aws/storage/index.js +1 -0
  186. package/dist/lib/resources/aws/storage/s3.js +10 -3
  187. package/dist/lib/resources/aws/utilities/customResource.js +18 -9
  188. package/dist/lib/synth_dump.d.ts +1 -0
  189. package/dist/lib/synth_dump.js +42 -0
  190. package/dist/lib/utils/cdkContext.d.ts +2 -0
  191. package/dist/lib/utils/cdkContext.js +4 -2
  192. package/dist/lib/utils/connections.js +6 -0
  193. package/dist/lib/utils/connector.d.ts +12 -0
  194. package/dist/lib/utils/costAllocationTags.d.ts +9 -0
  195. package/dist/lib/utils/costAllocationTags.js +11 -1
  196. package/dist/lib/utils/databaseTypes.d.ts +14 -0
  197. package/dist/lib/utils/getConfig.d.ts +2 -0
  198. package/dist/lib/utils/getConfig.js +2 -0
  199. package/dist/lib/utils/index.d.ts +1 -0
  200. package/dist/lib/utils/index.js +1 -0
  201. package/dist/lib/utils/manifestWriter.d.ts +6 -89
  202. package/dist/lib/utils/manifestWriter.js +36 -23
  203. package/dist/lib/utils/migrationVersionResolvers.d.ts +2 -0
  204. package/dist/lib/utils/migrationVersionResolvers.js +2 -0
  205. package/dist/lib/utils/orgConfigParser.js +2 -1
  206. package/dist/lib/utils/resolveAlertsTopic.d.ts +14 -0
  207. package/dist/lib/utils/resolveAlertsTopic.js +30 -0
  208. package/dist/lib/utils/validationLogger.js +6 -3
  209. package/package.json +22 -19
@@ -0,0 +1,181 @@
1
+ /** Database name created at ClickHouse bootstrap; consumed by BACKUP DATABASE,
2
+ * OPTIMIZE TABLE, and the DatabaseName CfnOutput so all four sites share one source. */
3
+ export const CLICKHOUSE_DATABASE_NAME = "analytics";
4
+ /** Default EC2 instance type for ClickHouse (Graviton3 — fixed-spec cores).
5
+ *
6
+ * `m7g.medium` (1 vCPU sustained, 4 GiB) chosen over `t4g.medium` (2 vCPU
7
+ * burst, 4 GiB) because CH's steady-state merge/scan/aggregation profile is
8
+ * not bursty — burst credits run out under the per-5min metrics ingest plus
9
+ * the OPTIMIZE FINAL sidecar, leaving the box throttled at baseline (10–20%
10
+ * of 2 vCPU) for the rest of the hour. Graviton3 is also ~25% faster on
11
+ * columnar scans than Graviton2.
12
+ *
13
+ * Settings are tuned for 1 vCPU sustained (max_threads=1 across all profiles,
14
+ * max_concurrent_queries=4) — see clickhouseUserData.ts. Bumping to a larger
15
+ * instance MUST be paired with raising those thread/concurrency caps.
16
+ *
17
+ * Next size up: `m7g.large` (2 vCPU, 8 GiB) for sustained workloads, or
18
+ * `r7g.medium` (1 vCPU, 8 GiB) when memory-bound. Set via
19
+ * `clickhouseInstanceType` CDK context or the `instanceType` prop, no code
20
+ * change needed (see clickhouseDatabase.ts:162). */
21
+ export const DEFAULT_CLICKHOUSE_INSTANCE_TYPE = "m7g.medium";
22
+ /** ClickHouse container image. Explicit `docker.io/` prefix is required so
23
+ * string-form consumers in `ecsImages.ts#getContainerImage()` route through
24
+ * `ContainerImage.fromRegistry(...)` instead of the ECR-repo fallback (which
25
+ * would otherwise append a service-tag suffix and produce a double-colon
26
+ * malformed image reference).
27
+ *
28
+ * Pin the full patch (not the floating `26.3`) so prod and local containers
29
+ * always run the same digest. The floating tag bit us once when CH 26.3.10
30
+ * tightened config.xml validation overnight while local Docker caches still
31
+ * held the prior patch — keep this pin in lockstep with
32
+ * `webapp/docker-compose.yaml`.
33
+ *
34
+ * Default Ubuntu (glibc) variant chosen over `-alpine` (musl): musl's
35
+ * threading/NPTL implementation is materially slower under CH's per-merge
36
+ * / per-query-stage thread fan-out, and musl's 128 KB default thread stack
37
+ * has a recurring history of stack-overflow incidents on deep query plans.
38
+ * Image size is ~250 MB larger but irrelevant on a single-instance EC2 ASG
39
+ * that pulls once per launch. Upstream CH CI runs its full perf + stress
40
+ * matrix on the Ubuntu build; Alpine is community-tier coverage. */
41
+ export const CLICKHOUSE_IMAGE = "docker.io/clickhouse/clickhouse-server:26.3.10.60";
42
+ /** EBS volume configuration. */
43
+ export const CLICKHOUSE_EBS_VOLUME_SIZE_GB = 80;
44
+ export const CLICKHOUSE_EBS_IOPS = 3000;
45
+ export const CLICKHOUSE_EBS_THROUGHPUT_MBPS = 125;
46
+ /** ECS task resource allocation. m7g.medium ships with 4 GB; reserve 1 GB
47
+ * for the host (kernel + ECS agent + cloudwatch agent + IMDS) and give
48
+ * ClickHouse the remaining 3 GB. Stays right when bumping to m7g.large
49
+ * (8 GB) — raise this value in lockstep, or fall back to deriving from
50
+ * the instance type if we ever support multiple sizes. */
51
+ export const CLICKHOUSE_TASK_MEMORY_MIB = 3072;
52
+ /** ClickHouse ports. */
53
+ export const CLICKHOUSE_HTTP_PORT = 8123;
54
+ export const CLICKHOUSE_NATIVE_PORT = 9000;
55
+ export const CLICKHOUSE_PROMETHEUS_PORT = 9363;
56
+ /** EBS device name for the data volume (must match user data script). */
57
+ export const CLICKHOUSE_EBS_DEVICE_NAME = "/dev/xvdf";
58
+ /** EBS mount path on the EC2 host. */
59
+ export const CLICKHOUSE_DATA_MOUNT_PATH = "/mnt/clickhouse-data";
60
+ /** Secrets Manager path prefix. */
61
+ export const CLICKHOUSE_SECRETS_PREFIX = "fjall/clickhouse";
62
+ /** Canonical Secrets Manager secret name for a ClickHouse user.
63
+ * Two sites compute this name and they MUST match:
64
+ * (a) the secret mint inside `createClickHouseUserSecret(...)` in
65
+ * `clickhouseDatabase.ts` — calls this helper directly;
66
+ * (b) the SSM live-reload script that fetches the admin password via
67
+ * `aws secretsmanager get-secret-value --secret-id` — also calls
68
+ * this helper directly.
69
+ * Drift between the two would silently break the live-reload path post-
70
+ * deploy on a users.d S3 change (no compile or test signal). */
71
+ export function clickHouseUserSecretName(userName) {
72
+ return `${CLICKHOUSE_SECRETS_PREFIX}/${userName.replace(/_/g, "-")}-password`;
73
+ }
74
+ /** ECS-injected env var name carrying a user's plaintext password.
75
+ * Canonical source is `@fjall/util/migration § userPasswordEnvName` — re-exported
76
+ * here so the construct-side import chain stays unchanged while the runner
77
+ * (`webapp/scripts/migration-runner.mjs`) and any future consumer share a single
78
+ * declaration. Coupled values per `.claude/rules/code-quality.md § "Coupled values:
79
+ * shared source at 2 occurrences"`. */
80
+ export { userPasswordEnvName } from "@fjall/util/migration";
81
+ /** Env var name carrying the SHA-256 of a user's plaintext password.
82
+ * The container entrypoint wrapper derives this name from the
83
+ * `USER_<NAME>_PASSWORD` env var injected by ECS `secretsImport`, then
84
+ * resolves `<password_sha256_hex from_env="..."/>` directives in
85
+ * `users.d/fjall.xml` against it at server start. */
86
+ export function userSha256EnvName(userName) {
87
+ return `USER_${userName.toUpperCase()}_SHA256`;
88
+ }
89
+ /** Canonical bash pipeline that hashes a ClickHouse user password.
90
+ * Invoked from the container entrypoint wrapper
91
+ * (`buildClickHouseEntrypointWrapper`) to derive `USER_<NAME>_SHA256` from
92
+ * the ECS-injected plaintext at server start.
93
+ * @param plaintextShellRef A quoted bash reference to the plaintext
94
+ * password (e.g. `"\${!var}"` for the wrapper's indirect deref). */
95
+ export function clickHousePasswordSha256Snippet(plaintextShellRef) {
96
+ return `printf '%s' ${plaintextShellRef} | sha256sum | awk '{print $1}'`;
97
+ }
98
+ /** Tag identifying ClickHouse server ASG instances.
99
+ * Three sites carry this pair and they MUST match: (a) the `tags:` map on
100
+ * the ClickHouse EC2 instance (threaded through to ASG instance tags),
101
+ * (b) the SSM `Targets: [{ Key: "tag:<key>", Values: ["<value>"] }]` filter
102
+ * on the live-reload `AwsCustomResource`, and (c) the synth snapshot tests
103
+ * pinning the relationship. Drift between (a) and (b) silently routes the
104
+ * reload command to zero instances — the `InvocationDoesNotExist` failure
105
+ * is already suppressed by `ignoreErrorCodesMatching`, so the misroute
106
+ * emits no signal. */
107
+ export const CLICKHOUSE_SERVER_ROLE_TAG = {
108
+ key: "ClickHouseRole",
109
+ value: "server"
110
+ };
111
+ /** Shared secret generation options (all ClickHouse users share the same policy). */
112
+ export const CLICKHOUSE_SECRET_OPTIONS = {
113
+ excludePunctuation: true,
114
+ passwordLength: 32
115
+ };
116
+ /** Health check configuration. */
117
+ export const CLICKHOUSE_HEALTH_CHECK = {
118
+ INTERVAL_SECONDS: 30,
119
+ TIMEOUT_SECONDS: 5,
120
+ RETRIES: 3,
121
+ START_PERIOD_SECONDS: 120
122
+ };
123
+ /** Container stop timeout in seconds. ECS waits this long after SIGTERM
124
+ * before sending SIGKILL. ClickHouse needs the longer-than-default window
125
+ * (CDK default: 30s) to flush in-progress merges, finish writing parts,
126
+ * and close the data directory cleanly — premature SIGKILL on a 1 vCPU
127
+ * m7g.medium under merge load risks `max_suspicious_broken_parts` on the
128
+ * next start. ECS upper bound is 120s. */
129
+ export const CLICKHOUSE_STOP_TIMEOUT_SECONDS = 90;
130
+ /** OPTIMIZE TABLE FINAL schedule.
131
+ * RMT tables carry min_age_to_force_merge_seconds=600 so the engine already merges
132
+ * old parts within 10 min; this task is a safety net for MVs (no engine-level setting)
133
+ * and for ReplacingMergeTree dedup under skewed write patterns. 6 hours is sufficient. */
134
+ export const OPTIMISE_FINAL_SCHEDULE = "rate(6 hours)";
135
+ /** Tables requiring periodic OPTIMIZE FINAL (ReplacingMergeTree only).
136
+ * Keep in sync with REPLACING_MERGE_TREE_TABLES in
137
+ * webapp/app/.server/lib/clickhouse/tenantQuery/tableConstants.ts (auto-FINAL). */
138
+ export const REPLACING_MERGE_TREE_TABLES = [
139
+ "application_metrics",
140
+ "cost_records",
141
+ "log_fingerprints",
142
+ "insights",
143
+ "asset_inventory"
144
+ ];
145
+ /** Subdirectory on the EBS volume for server config files (must match CDK volume mount). */
146
+ export const CLICKHOUSE_CONFIG_SUBDIR = "server-config.d";
147
+ /** Subdirectory on the EBS volume for users config files (must match CDK volume mount). */
148
+ export const CLICKHOUSE_USERS_SUBDIR = "server-users.d";
149
+ /** Cloud Map service name (resolves to `clickhouse.<appName>.local` via the per-app namespace registered by `App.getInstance().registerService(...)`). */
150
+ export const CLICKHOUSE_CLOUDMAP_SERVICE_NAME = "clickhouse";
151
+ /** ECS container name for the ClickHouse server task. Threaded through both
152
+ * the `addContainer` call site and the SSM reload script (which uses
153
+ * `docker ps --filter "label=com.amazonaws.ecs.container-name=<name>"` to
154
+ * locate the running container). Drift between the two sites turns the
155
+ * live-reload path into a silent no-op without any compile/test signal,
156
+ * so this lives as a single source of truth. */
157
+ export const CLICKHOUSE_SERVER_CONTAINER_NAME = "clickhouse";
158
+ /** Materialised views that benefit from periodic OPTIMIZE to reduce part count at read time.
159
+ * These are not ReplacingMergeTree (no dedup needed) but un-merged parts force
160
+ * read-time aggregation which degrades query performance. */
161
+ export const OPTIMISE_MV_TABLES = [
162
+ "metrics_hourly_mv",
163
+ "metrics_daily_mv",
164
+ "response_time_quantiles_hourly_mv",
165
+ "deployment_duration_quantiles_daily_mv",
166
+ "log_severity_hourly_mv",
167
+ "compliance_score_daily_mv",
168
+ "ai_usage_daily_mv",
169
+ "finding_daily_aggregate",
170
+ "insight_pattern_dismissals"
171
+ ];
172
+ /** Resource allocation for the lightweight optimise task. */
173
+ export const OPTIMISE_TASK_MEMORY_MIB = 256;
174
+ export const OPTIMISE_TASK_CPU_UNITS = 256;
175
+ /** Automated backup schedule (daily 03:00 UTC — low-traffic window). */
176
+ export const BACKUP_SCHEDULE = "cron(0 3 * * ? *)";
177
+ /** Resource allocation for the backup task (lightweight — clickhouse-client only). */
178
+ export const BACKUP_TASK_MEMORY_MIB = 256;
179
+ export const BACKUP_TASK_CPU_UNITS = 256;
180
+ /** Backup object expiration: 14 days (retains 14 daily snapshots). */
181
+ export const BACKUP_RETENTION_DAYS = 14;
@@ -0,0 +1,71 @@
1
+ import { z } from "zod";
2
+ /**
3
+ * Schema-admin user — the framework's bootstrap-privileged identity that
4
+ * customer SQL runs as. Owns DDL, schema migrations, GRANTs. Rendered into
5
+ * `users.d/fjall.xml` with `<access_management>1</access_management>` so it
6
+ * can manage GRANT / CREATE USER from SQL.
7
+ *
8
+ * The `profile` field names a key in the construct's `profiles:` prop (or
9
+ * `ClickHouseDefaultProfiles` if omitted) and is bound directly in the XML
10
+ * `<user><profile>…</profile></user>` block — not via the migration helper.
11
+ */
12
+ export declare const ClickHouseSchemaAdminSchema: z.ZodObject<{
13
+ name: z.ZodString;
14
+ profile: z.ZodString;
15
+ }, z.core.$strict>;
16
+ export type ClickHouseSchemaAdmin = z.infer<typeof ClickHouseSchemaAdminSchema>;
17
+ /**
18
+ * Managed-password workload user name. The framework mints a secret, grants
19
+ * IAM read on it, and the migration helper issues `CREATE USER … IDENTIFIED
20
+ * WITH sha256_password …` at runtime. Profile binding is NOT performed by
21
+ * the framework — customer SQL applies `ALTER USER <name> SETTINGS PROFILE
22
+ * <profile>` alongside their GRANTs.
23
+ */
24
+ export declare const ManagedPasswordNameSchema: z.ZodString;
25
+ export type ManagedPasswordName = z.infer<typeof ManagedPasswordNameSchema>;
26
+ /**
27
+ * Per-profile resource-cap shape. Field names mirror the ClickHouse XML
28
+ * element names (snake_case at the wire; camelCase here for TS ergonomics).
29
+ * Every field is `.optional()` — a profile may declare any subset.
30
+ *
31
+ * `renderUsersXml` maps each present field to the snake_case XML element
32
+ * via `camelToSnakeCase`. Unknown fields are rejected by `.strict()` so a
33
+ * typo at the consumer doesn't silently ship a no-op profile.
34
+ */
35
+ export declare const ProfileSpecSchema: z.ZodObject<{
36
+ maxThreads: z.ZodOptional<z.ZodNumber>;
37
+ maxInsertThreads: z.ZodOptional<z.ZodNumber>;
38
+ maxConcurrentQueriesForUser: z.ZodOptional<z.ZodNumber>;
39
+ maxMemoryUsage: z.ZodOptional<z.ZodNumber>;
40
+ maxMemoryUsageForUser: z.ZodOptional<z.ZodNumber>;
41
+ maxExecutionTime: z.ZodOptional<z.ZodNumber>;
42
+ maxRowsToRead: z.ZodOptional<z.ZodNumber>;
43
+ maxBytesBeforeExternalSort: z.ZodOptional<z.ZodNumber>;
44
+ maxBytesBeforeExternalGroupBy: z.ZodOptional<z.ZodNumber>;
45
+ logQueriesMinQueryDurationMs: z.ZodOptional<z.ZodNumber>;
46
+ optimizeMoveToPrewhere: z.ZodOptional<z.ZodBoolean>;
47
+ useQueryConditionCache: z.ZodOptional<z.ZodBoolean>;
48
+ useSkipIndexesIfFinal: z.ZodOptional<z.ZodBoolean>;
49
+ applyRowPolicyAfterFinal: z.ZodOptional<z.ZodBoolean>;
50
+ applyPrewhereAfterFinal: z.ZodOptional<z.ZodBoolean>;
51
+ doNotMergeAcrossPartitionsSelectFinal: z.ZodOptional<z.ZodBoolean>;
52
+ asyncInsert: z.ZodOptional<z.ZodBoolean>;
53
+ waitForAsyncInsert: z.ZodOptional<z.ZodBoolean>;
54
+ asyncInsertMaxDataSize: z.ZodOptional<z.ZodNumber>;
55
+ asyncInsertBusyTimeoutMinMs: z.ZodOptional<z.ZodNumber>;
56
+ asyncInsertBusyTimeoutMaxMs: z.ZodOptional<z.ZodNumber>;
57
+ asyncInsertUseAdaptiveBusyTimeout: z.ZodOptional<z.ZodBoolean>;
58
+ asyncInsertDeduplicate: z.ZodOptional<z.ZodBoolean>;
59
+ inputFormatParallelParsing: z.ZodOptional<z.ZodBoolean>;
60
+ outputFormatParallelFormatting: z.ZodOptional<z.ZodBoolean>;
61
+ queryPlanOptimizeLazyMaterialization: z.ZodOptional<z.ZodBoolean>;
62
+ materializeTtlAfterModify: z.ZodOptional<z.ZodBoolean>;
63
+ }, z.core.$strict>;
64
+ export type ProfileSpec = z.infer<typeof ProfileSpecSchema>;
65
+ /**
66
+ * Re-exported for the construct's Stage 1 validation. Profile keys MUST
67
+ * match the same lowercase snake_case shape as user names — they emit as
68
+ * XML element names with no escaping.
69
+ */
70
+ export declare const PROFILE_NAME_PATTERN: RegExp;
71
+ export declare const ClickHouseDefaultProfiles: Readonly<Record<string, Readonly<ProfileSpec>>>;
@@ -0,0 +1,157 @@
1
+ import { z } from "zod";
2
+ /** ClickHouse user/profile name pattern — lowercase snake_case, leading
3
+ * letter only. Shared by `ClickHouseUserSchema.name` and the per-profile
4
+ * key regex applied inside `ClickHouseDatabase`'s construct-time validation
5
+ * (Stage 1). Keeps the XML element names emit-safe (no escaping needed). */
6
+ const NAME_PATTERN = /^[a-z][a-z0-9_]*$/;
7
+ /**
8
+ * Schema-admin user — the framework's bootstrap-privileged identity that
9
+ * customer SQL runs as. Owns DDL, schema migrations, GRANTs. Rendered into
10
+ * `users.d/fjall.xml` with `<access_management>1</access_management>` so it
11
+ * can manage GRANT / CREATE USER from SQL.
12
+ *
13
+ * The `profile` field names a key in the construct's `profiles:` prop (or
14
+ * `ClickHouseDefaultProfiles` if omitted) and is bound directly in the XML
15
+ * `<user><profile>…</profile></user>` block — not via the migration helper.
16
+ */
17
+ export const ClickHouseSchemaAdminSchema = z
18
+ .object({
19
+ name: z
20
+ .string()
21
+ .min(1)
22
+ .max(63)
23
+ .regex(NAME_PATTERN, "Must be lowercase snake_case"),
24
+ profile: z.string().min(1)
25
+ })
26
+ .strict();
27
+ /**
28
+ * Managed-password workload user name. The framework mints a secret, grants
29
+ * IAM read on it, and the migration helper issues `CREATE USER … IDENTIFIED
30
+ * WITH sha256_password …` at runtime. Profile binding is NOT performed by
31
+ * the framework — customer SQL applies `ALTER USER <name> SETTINGS PROFILE
32
+ * <profile>` alongside their GRANTs.
33
+ */
34
+ export const ManagedPasswordNameSchema = z
35
+ .string()
36
+ .min(1)
37
+ .max(63)
38
+ .regex(NAME_PATTERN, "Must be lowercase snake_case");
39
+ /**
40
+ * Per-profile resource-cap shape. Field names mirror the ClickHouse XML
41
+ * element names (snake_case at the wire; camelCase here for TS ergonomics).
42
+ * Every field is `.optional()` — a profile may declare any subset.
43
+ *
44
+ * `renderUsersXml` maps each present field to the snake_case XML element
45
+ * via `camelToSnakeCase`. Unknown fields are rejected by `.strict()` so a
46
+ * typo at the consumer doesn't silently ship a no-op profile.
47
+ */
48
+ export const ProfileSpecSchema = z
49
+ .object({
50
+ maxThreads: z.number().int().positive().optional(),
51
+ maxInsertThreads: z.number().int().positive().optional(),
52
+ maxConcurrentQueriesForUser: z.number().int().positive().optional(),
53
+ maxMemoryUsage: z.number().int().positive().optional(),
54
+ maxMemoryUsageForUser: z.number().int().positive().optional(),
55
+ maxExecutionTime: z.number().int().positive().optional(),
56
+ maxRowsToRead: z.number().int().positive().optional(),
57
+ maxBytesBeforeExternalSort: z.number().int().positive().optional(),
58
+ maxBytesBeforeExternalGroupBy: z.number().int().positive().optional(),
59
+ logQueriesMinQueryDurationMs: z.number().int().nonnegative().optional(),
60
+ optimizeMoveToPrewhere: z.boolean().optional(),
61
+ useQueryConditionCache: z.boolean().optional(),
62
+ useSkipIndexesIfFinal: z.boolean().optional(),
63
+ applyRowPolicyAfterFinal: z.boolean().optional(),
64
+ applyPrewhereAfterFinal: z.boolean().optional(),
65
+ doNotMergeAcrossPartitionsSelectFinal: z.boolean().optional(),
66
+ asyncInsert: z.boolean().optional(),
67
+ waitForAsyncInsert: z.boolean().optional(),
68
+ asyncInsertMaxDataSize: z.number().int().positive().optional(),
69
+ asyncInsertBusyTimeoutMinMs: z.number().int().nonnegative().optional(),
70
+ asyncInsertBusyTimeoutMaxMs: z.number().int().nonnegative().optional(),
71
+ asyncInsertUseAdaptiveBusyTimeout: z.boolean().optional(),
72
+ asyncInsertDeduplicate: z.boolean().optional(),
73
+ inputFormatParallelParsing: z.boolean().optional(),
74
+ outputFormatParallelFormatting: z.boolean().optional(),
75
+ queryPlanOptimizeLazyMaterialization: z.boolean().optional(),
76
+ materializeTtlAfterModify: z.boolean().optional()
77
+ })
78
+ .strict();
79
+ /**
80
+ * Re-exported for the construct's Stage 1 validation. Profile keys MUST
81
+ * match the same lowercase snake_case shape as user names — they emit as
82
+ * XML element names with no escaping.
83
+ */
84
+ export const PROFILE_NAME_PATTERN = NAME_PATTERN;
85
+ /**
86
+ * Four workload-class default profiles. Cap values lifted byte-for-byte
87
+ * from the pre-Phase-4b inline `<app_writer>` / `<audit_writer>` /
88
+ * `<backup_reader>` / `<schema_admin>` blocks at
89
+ * `clickhouseUserData.ts:296-382` — zero behavioural drift from the
90
+ * pre-4b prod resource caps.
91
+ *
92
+ * Frozen via `Object.freeze` on the outer record AND each sub-object so
93
+ * a consumer cannot mutate the defaults in place. Pass your own
94
+ * `profiles:` map to the construct to extend / override; never mutate
95
+ * this constant.
96
+ */
97
+ const _ClickHouseDefaultProfilesRaw = {
98
+ high_throughput_ingest: {
99
+ maxThreads: 1,
100
+ maxInsertThreads: 1,
101
+ maxConcurrentQueriesForUser: 2,
102
+ logQueriesMinQueryDurationMs: 100,
103
+ optimizeMoveToPrewhere: true,
104
+ useQueryConditionCache: true,
105
+ useSkipIndexesIfFinal: true,
106
+ applyRowPolicyAfterFinal: true,
107
+ applyPrewhereAfterFinal: true,
108
+ doNotMergeAcrossPartitionsSelectFinal: true,
109
+ asyncInsert: true,
110
+ waitForAsyncInsert: true,
111
+ asyncInsertMaxDataSize: 10000000,
112
+ asyncInsertBusyTimeoutMinMs: 50,
113
+ asyncInsertBusyTimeoutMaxMs: 2000,
114
+ asyncInsertUseAdaptiveBusyTimeout: true,
115
+ asyncInsertDeduplicate: true,
116
+ inputFormatParallelParsing: false,
117
+ outputFormatParallelFormatting: false,
118
+ queryPlanOptimizeLazyMaterialization: true,
119
+ maxMemoryUsage: 1610612736,
120
+ maxMemoryUsageForUser: 2147483648,
121
+ maxBytesBeforeExternalSort: 536870912,
122
+ maxBytesBeforeExternalGroupBy: 536870912,
123
+ maxExecutionTime: 30,
124
+ maxRowsToRead: 10000000
125
+ },
126
+ audit_append: {
127
+ maxThreads: 1,
128
+ maxInsertThreads: 1,
129
+ maxConcurrentQueriesForUser: 2,
130
+ maxMemoryUsage: 500000000,
131
+ maxExecutionTime: 10,
132
+ asyncInsert: true,
133
+ waitForAsyncInsert: true
134
+ },
135
+ read_only: {
136
+ maxThreads: 1,
137
+ maxConcurrentQueriesForUser: 1,
138
+ maxMemoryUsage: 1000000000,
139
+ maxExecutionTime: 3600
140
+ },
141
+ ddl_admin: {
142
+ maxThreads: 1,
143
+ maxConcurrentQueriesForUser: 1,
144
+ maxMemoryUsage: 1000000000,
145
+ maxExecutionTime: 1800
146
+ }
147
+ };
148
+ export const ClickHouseDefaultProfiles = Object.freeze({
149
+ high_throughput_ingest: Object.freeze({
150
+ ..._ClickHouseDefaultProfilesRaw.high_throughput_ingest
151
+ }),
152
+ audit_append: Object.freeze({
153
+ ..._ClickHouseDefaultProfilesRaw.audit_append
154
+ }),
155
+ read_only: Object.freeze({ ..._ClickHouseDefaultProfilesRaw.read_only }),
156
+ ddl_admin: Object.freeze({ ..._ClickHouseDefaultProfilesRaw.ddl_admin })
157
+ });
@@ -0,0 +1,14 @@
1
+ import { type IVpc } from "aws-cdk-lib/aws-ec2";
2
+ import type { Construct } from "constructs";
3
+ import { SecurityGroup } from "../networking/securityGroup.js";
4
+ /**
5
+ * Create the ClickHouse security group.
6
+ *
7
+ * No consumer ingress rules are added here — consumer ingress flows through
8
+ * the connector contract (`ISecurityGroupConnector` + `additionalTcpPorts`)
9
+ * so the SG is consumer-agnostic and reusable across compute consumers.
10
+ *
11
+ * Self-referencing native-port ingress is kept so the optimise/backup
12
+ * scheduled tasks (which share this SG) can reach the ClickHouse server.
13
+ */
14
+ export declare function createClickHouseSecurityGroup(scope: Construct, vpc: IVpc, nativePort: number): SecurityGroup;
@@ -0,0 +1,23 @@
1
+ import { Peer, Port } from "aws-cdk-lib/aws-ec2";
2
+ import { SecurityGroup } from "../networking/securityGroup.js";
3
+ /**
4
+ * Create the ClickHouse security group.
5
+ *
6
+ * No consumer ingress rules are added here — consumer ingress flows through
7
+ * the connector contract (`ISecurityGroupConnector` + `additionalTcpPorts`)
8
+ * so the SG is consumer-agnostic and reusable across compute consumers.
9
+ *
10
+ * Self-referencing native-port ingress is kept so the optimise/backup
11
+ * scheduled tasks (which share this SG) can reach the ClickHouse server.
12
+ */
13
+ export function createClickHouseSecurityGroup(scope, vpc, nativePort) {
14
+ const sg = new SecurityGroup(scope, "ClickHouseSecurityGroup", {
15
+ vpc,
16
+ description: "Security group for ClickHouse analytics instance",
17
+ allowAllOutbound: false
18
+ });
19
+ sg.addIngressRule(sg, Port.tcp(nativePort), "ClickHouse native protocol from sidecar tasks (self-referencing)");
20
+ sg.addEgressRule(Peer.anyIpv4(), Port.tcp(443), "HTTPS to AWS service endpoints (S3, Secrets Manager, IMDS)");
21
+ sg.addEgressRule(sg, Port.tcp(nativePort), "ClickHouse native protocol for sidecar tasks");
22
+ return sg;
23
+ }
@@ -0,0 +1,69 @@
1
+ import type { ClickHouseSchemaAdmin, ProfileSpec } from "./clickhouseSchemas.js";
2
+ /**
3
+ * Options for {@link buildClickHouseUserData}.
4
+ *
5
+ * Per design D27 + D12: Cloudflare R2 is dropped; cold storage targets S3
6
+ * with IAM-only auth via `<use_environment_credentials>true</use_environment_credentials>`.
7
+ * Zero env-var access keys are injected into the rendered config.
8
+ */
9
+ export interface BuildClickHouseUserDataOptions {
10
+ /** S3 bucket name for `BACKUP DATABASE TO S3`; pre-authenticated via IAM. */
11
+ backupBucketName: string;
12
+ /** AWS region of the backup bucket. */
13
+ backupBucketRegion: string;
14
+ /**
15
+ * Optional S3 cold-tier configuration.
16
+ * When omitted, single-tier hot storage on the EC2 EBS volume is used.
17
+ */
18
+ coldTier?: {
19
+ bucketName: string;
20
+ region: string;
21
+ };
22
+ }
23
+ export declare function generateServerConfigXml(options: BuildClickHouseUserDataOptions): string;
24
+ export interface GenerateUsersConfigXmlOptions {
25
+ schemaAdmin: ClickHouseSchemaAdmin;
26
+ profiles: Record<string, ProfileSpec>;
27
+ vpcCidr: string;
28
+ }
29
+ /**
30
+ * Construct-side thin wrapper around `renderUsersXml`. Always emits the
31
+ * prod shape: loopback-only `<default>` user, `from_env=` sha256 password
32
+ * directives. The container entrypoint wrapper (see
33
+ * `buildClickHouseEntrypointWrapper`) resolves the `USER_<NAME>_SHA256` env
34
+ * vars at server start from `sha256sum` of each ECS-injected plaintext.
35
+ *
36
+ * TODO secondary CIDRs: props.vpc.vpcCidrBlock returns only the primary CIDR.
37
+ * BYOC consumers with multi-CIDR VPCs need a per-CIDR `<ip>` emitter — see
38
+ * designs/2026-05-12-clickhouse-construct-leak-fixes.md § "Phase 1.5".
39
+ */
40
+ export declare function generateUsersConfigXml(options: GenerateUsersConfigXmlOptions): string;
41
+ /**
42
+ * Build the EC2 user-data script that bootstraps ClickHouse on the ASG instance.
43
+ *
44
+ * Per design D27 + D12: cold-storage and backups authenticate to S3 via IAM
45
+ * (the EC2 instance role's grants), not via env-var access keys. The rendered
46
+ * output contains zero `R2_*` / `AWS_ACCESS_KEY_ID` / `AWS_SECRET_ACCESS_KEY`
47
+ * references — `<use_environment_credentials>true</use_environment_credentials>`
48
+ * tells ClickHouse to use the IMDS-derived credentials instead.
49
+ */
50
+ export declare function buildClickHouseUserData(options: BuildClickHouseUserDataOptions): string;
51
+ /**
52
+ * Build the bash wrapper executed as the CH container's entrypoint.
53
+ *
54
+ * The CH server reads `users.d/fjall.xml`, which carries
55
+ * `<password_sha256_hex from_env="USER_<NAME>_SHA256"/>` directives, and
56
+ * resolves those `from_env=` lookups against the server process's
57
+ * environment at startup. ECS injects the plaintext password for each user
58
+ * as `USER_<NAME>_PASSWORD` via `secretsImport`; this wrapper derives the
59
+ * matching `USER_<NAME>_SHA256` env var (via the same SHA-256 pipeline as
60
+ * the EC2 user-data loop in `buildClickHouseUserData()`) and then `exec`s
61
+ * the stock CH entrypoint so the server inherits the derived vars after
62
+ * the gosu privilege drop.
63
+ *
64
+ * Pure-bash iteration via `${!USER_@}` avoids spawning `env`/`grep` and
65
+ * guarantees only ECS-injected secrets are considered. The `case` filter
66
+ * skips any future `USER_*_HOST`/`USER_*_PORT` envs that don't match the
67
+ * password convention.
68
+ */
69
+ export declare function buildClickHouseEntrypointWrapper(): string;