canopycms-cdk 0.0.67-int.88 → 0.0.67-int.90

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.
@@ -755,8 +755,8 @@ export class AssetSupport extends Construct {
755
755
  code: lambda.Code.fromAsset(transformAssetDir),
756
756
  handler: 'handler.handler',
757
757
  // nodejs20.x was deprecated 2026-04-30; CDK's CloudFormation validation
758
- // now fails synth on it. The esbuild bundle targets node20 and runs
759
- // unchanged on the node22 runtime.
758
+ // now fails synth on it. The esbuild bundle (lambda/asset-transform/
759
+ // build.mjs) targets node22 to match; move the two together.
760
760
  runtime: lambda.Runtime.NODEJS_22_X,
761
761
  architecture: lambda.Architecture.ARM_64,
762
762
  memorySize: TRANSFORM_LAMBDA_MEMORY_MB,
@@ -34,11 +34,18 @@ export interface CanopyCmsServiceProps {
34
34
  /** Lambda reserved concurrency cap (default: 10) */
35
35
  reservedConcurrency?: number;
36
36
  /**
37
- * Lambda architecture (default: `Architecture.X86_64`, Lambda's own
38
- * default). MUST match the platform the Docker image was built for - e.g.
39
- * an image built for `Platform.LINUX_ARM64` requires
40
- * `Architecture.ARM_64` here, or the function fails at invoke time with
41
- * an exec format error.
37
+ * Lambda architecture (default: `Architecture.ARM_64`, matching the EC2
38
+ * worker and AssetSupport's transform Lambda).
39
+ *
40
+ * This also decides the image's architecture for
41
+ * `DockerImageCode.fromImageAsset`: the construct always passes a resolved
42
+ * architecture to the function, and CDK derives the Docker build platform
43
+ * from it. So omit `platform` on `fromImageAsset`. An explicit `platform`
44
+ * overrides the derived one, and an image built for the other architecture
45
+ * cannot run on the function: an arm64 image on an x86_64 function fails at
46
+ * invoke with `Runtime.InvalidEntrypoint` (see "Where the image is built" in
47
+ * docs/deploying-to-aws.md). A prebuilt image (`DockerImageCode.fromEcr`) has
48
+ * no build for CDK to steer, so it must already be built for this architecture.
42
49
  */
43
50
  architecture?: lambda.Architecture;
44
51
  /** EC2 spot max price (default: on-demand rate for t4g.nano) */
@@ -70,8 +77,114 @@ export interface CanopyCmsServiceProps {
70
77
  githubRepo: string;
71
78
  /** Secrets Manager ARN for the GitHub bot token */
72
79
  githubTokenSecretArn?: string;
80
+ /**
81
+ * The key within a JSON secret document at `githubTokenSecretArn`; omit when
82
+ * the secret's whole value is the credential.
83
+ *
84
+ * Stamped into the worker's `CANOPYCMS_GITHUB_TOKEN_SECRET_JSON_FIELD`, which
85
+ * `getSecret` (packages/canopycms-cdk/worker/secrets.ts) reads to pull one
86
+ * field out of the document instead of using the whole string. Omitting it is
87
+ * the default and the common case — a secret holding a bare `ghp_…` needs
88
+ * nothing here.
89
+ *
90
+ * This is NOT the ECS/CloudFormation `arn:…:secret:name-AbCdEf:KEY::`
91
+ * convention. The worker calls the `GetSecretValue` API, which does not parse
92
+ * that suffix; a literal ARN carrying one is refused at synth (see
93
+ * `assertSecretArnHasNoFieldSuffix` below, and the known limit recorded on
94
+ * `SECRET_ARN_WITH_EMPTY_VERSION_TAIL` for the one token spelling that gets
95
+ * through) and the field belongs here instead.
96
+ */
97
+ githubTokenSecretJsonField?: string;
98
+ /**
99
+ * GitHub App ID, to authenticate the worker as a GitHub App installation
100
+ * instead of as a personal access token.
101
+ *
102
+ * **The token is the default and stays first-class.** Registering a GitHub
103
+ * App under an organisation takes an owner of that organisation (or a GitHub
104
+ * App manager for all its Apps), which many adopters are not, so this is the
105
+ * "if your organisation requires it" option, not a direction of travel.
106
+ * Nothing about
107
+ * `githubTokenSecretArn` is deprecated or warned about.
108
+ *
109
+ * All three App props (`githubAppId`, `githubAppInstallationId`,
110
+ * `githubAppPrivateKeySecretArn`) are set together or not at all, and App
111
+ * auth is mutually exclusive with `githubTokenSecretArn` — both are refused
112
+ * at synth. That mirrors `resolveWorkerGitHubAuth` in core
113
+ * (packages/canopycms/src/worker/github-auth.ts), which refuses the same two
114
+ * shapes at boot; checking here turns a 5-second systemd restart loop into a
115
+ * failed `cdk synth`.
116
+ *
117
+ * Stamped into the worker's `CANOPYCMS_GITHUB_APP_ID`.
118
+ */
119
+ githubAppId?: string;
120
+ /**
121
+ * The App's installation ID on your repository — NOT the App ID above.
122
+ *
123
+ * An App can be installed on several accounts, and a token is minted per
124
+ * installation, so both numbers are needed. It is the trailing number in the
125
+ * URL of the App's install page under your organisation's settings.
126
+ *
127
+ * Stamped into the worker's `CANOPYCMS_GITHUB_APP_INSTALLATION_ID`.
128
+ */
129
+ githubAppInstallationId?: string;
130
+ /**
131
+ * Secrets Manager ARN for the App's PEM private key.
132
+ *
133
+ * **ARN-only: there is deliberately no plaintext prop for this key**, unlike
134
+ * every other credential the construct knows about, and the reason is
135
+ * mechanical rather than a matter of taste. Every value the construct puts in
136
+ * the worker's environment is written into a `.env` file that systemd reads
137
+ * as `EnvironmentFile=`, where a newline starts a new variable — so
138
+ * `assertEnvSafe` refuses one, and a PEM is inherently multi-line. A
139
+ * plaintext key could not be delivered to the worker intact by this path at
140
+ * all. Passing the PEM itself here is caught by name at synth (see
141
+ * `assertNotInlinePrivateKey`, and the note there about the generated stack
142
+ * reaching CDK's own ARN complaint first) rather than surfacing as a puzzling
143
+ * "an ARN must not contain a newline".
144
+ *
145
+ * The ARN is unioned into the worker's IAM policy alongside the other secret
146
+ * ARN props; you do not need to repeat it in `secretsArns`.
147
+ */
148
+ githubAppPrivateKeySecretArn?: string;
149
+ /**
150
+ * The key within a JSON secret document at `githubAppPrivateKeySecretArn`;
151
+ * omit when the secret's whole value is the PEM.
152
+ *
153
+ * Stamped into the worker's
154
+ * `CANOPYCMS_GITHUB_APP_PRIVATE_KEY_SECRET_JSON_FIELD`. See
155
+ * `githubTokenSecretJsonField` above — same mechanism, same non-relationship
156
+ * to the ECS `:KEY::` ARN suffix. This is the case that motivated the JSON
157
+ * field support in the first place: an App private key is exactly the kind of
158
+ * material an organisation keeps inside one credential document per
159
+ * environment.
160
+ *
161
+ * A PEM stored as a JSON string value carries its newlines as `\n` escapes,
162
+ * which `JSON.parse` turns back into real newlines — and the worker
163
+ * additionally runs whatever it reads through
164
+ * `normalizeGitHubAppPrivateKey`, which unescapes and base64-unwraps, so a
165
+ * key mangled by a single-line config field still works.
166
+ */
167
+ githubAppPrivateKeySecretJsonField?: string;
73
168
  /** Secrets Manager ARN for the Clerk secret key */
74
169
  clerkSecretKeySecretArn?: string;
170
+ /**
171
+ * The key within a JSON secret document at `clerkSecretKeySecretArn`; omit
172
+ * when the secret's whole value is the credential.
173
+ *
174
+ * Stamped into the worker's `CLERK_SECRET_KEY_SECRET_JSON_FIELD`. See
175
+ * `githubTokenSecretJsonField` above — same mechanism, same non-relationship
176
+ * to the ECS `:KEY::` ARN suffix.
177
+ *
178
+ * Note that this covers `CLERK_SECRET_KEY` only. A Clerk JSON document
179
+ * typically also holds `CLERK_JWT_KEY` and the publishable key, and NEITHER
180
+ * can be sourced from Secrets Manager at all: `CLERK_JWT_KEY` reaches the
181
+ * Lambda as a plain value through `environment`, and the publishable key is a
182
+ * Docker build arg inlined into the client bundle. Both are public material
183
+ * (docs/deploying-to-aws.md, "Security Model"), so that is by design rather
184
+ * than an omission — but it means pointing this prop at your document does
185
+ * not relieve you of supplying those two separately.
186
+ */
187
+ clerkSecretKeySecretJsonField?: string;
75
188
  /**
76
189
  * The GitHub repository's default branch name (default: 'main').
77
190
  *
@@ -227,10 +340,10 @@ export interface CanopyCmsServiceProps {
227
340
  * - Lambda function (Docker image, EFS mount, private subnet, no internet)
228
341
  * - Lambda Function URL (for CloudFront origin)
229
342
  * - EC2 Worker (t4g.nano spot in ASG, public subnet, EFS mount, systemd) -
230
- * rolled on every deploy via the ASG's UpdatePolicy, so a changed worker
231
- * bundle actually reaches the instance instead of sitting unused in a
232
- * launch template until the next spot interruption (see the UpdatePolicy
233
- * below)
343
+ * rolled via the ASG's UpdatePolicy by every deploy that changes its launch
344
+ * template, so a changed worker bundle reaches the instance instead of
345
+ * sitting unused in a launch template until the next spot interruption (see
346
+ * the UpdatePolicy below)
234
347
  * - Dedicated CloudWatch log groups for the CMS Lambda and the worker's
235
348
  * stdout/stderr (the worker's is shipped via the amazon-cloudwatch-agent -
236
349
  * journald is not agent-readable), each with a custom name/retention/