@caelo-cms/provisioning 0.6.5 → 0.7.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@caelo-cms/provisioning",
3
- "version": "0.6.5",
3
+ "version": "0.7.0",
4
4
  "private": false,
5
5
  "license": "MPL-2.0",
6
6
  "description": "Pulumi-driven provisioning for Caelo CMS — `bunx @caelo-cms/provisioning --provider <self-hosted|gcp|aws|azure>` brings up a TLS-served install with Postgres + storage + secrets manager + the admin + the API gateway.",
@@ -38,7 +38,7 @@
38
38
  "prepublishOnly": "tsc -b --force && tsc --project stacks/gcp/tsconfig.json && tsc --project stacks/gcp-firebase/tsconfig.json"
39
39
  },
40
40
  "dependencies": {
41
- "@caelo-cms/shared": "^0.6.5",
41
+ "@caelo-cms/shared": "^0.7.0",
42
42
  "@clack/prompts": "1.3.0",
43
43
  "@pulumi/aws": "^7",
44
44
  "@pulumi/awsx": "^3",
@@ -141,7 +141,13 @@ const caeloSecretKek = pulumi.secret(kekBytes.hex);
141
141
  // Pre-v0.3.2 the wizard prompted for a key + Pulumi provisioned a
142
142
  // Secret + SecretVersion, but the Cloud Run service never read it
143
143
  // (no env-var mount existed). Dead code path removed.
144
- const resendApiKeyConfig = cfg.getSecret("resendApiKey");
144
+ // v0.6.6 — resend-api-key secret + IAM binding removed. The Resend
145
+ // API key now lives in the email_config DB row (set via the Owner UI
146
+ // /security/email/pending flow per email_config_pending.ts), not in
147
+ // Secret Manager. The Pulumi-managed secret + binding were scaffolded
148
+ // in v0.3.0 but never wired to runtime (transport.ts reads from
149
+ // cfg.config.apiKey, never from Secret Manager). Drop to reduce
150
+ // surface area + monthly cost.
145
151
 
146
152
  interface MadeSecret {
147
153
  resource: gcp.secretmanager.Secret;
@@ -167,7 +173,6 @@ const pgSecret = makeSecret("postgres-password", postgresPassword);
167
173
  const csrfSecretRes = makeSecret("csrf-secret", csrfSecret);
168
174
  const cookieSecretRes = makeSecret("cookie-secret", cookieSecret);
169
175
  const kekSecret = makeSecret("secret-kek", caeloSecretKek);
170
- const resendSecretRes = makeSecret("resend-api-key", resendApiKeyConfig ?? null);
171
176
 
172
177
  // =========================================================================
173
178
  // Tier 4 — Cloud SQL Postgres (private IP only; HA configurable)
@@ -264,7 +269,6 @@ for (const made of [
264
269
  { name: "csrf-secret", made: csrfSecretRes },
265
270
  { name: "cookie-secret", made: cookieSecretRes },
266
271
  { name: "secret-kek", made: kekSecret },
267
- { name: "resend-api-key", made: resendSecretRes },
268
272
  ]) {
269
273
  new gcp.secretmanager.SecretIamMember(
270
274
  `${namePrefix}-${made.name}-binding`,
@@ -300,6 +304,35 @@ new gcp.projects.IAMMember(
300
304
  opts,
301
305
  );
302
306
 
307
+ // v0.6.6 — Cloud Run telemetry IAM. When a Cloud Run service is
308
+ // assigned a CUSTOM runtime SA (serviceAccount: runSa.email below),
309
+ // Cloud Run does NOT automatically grant that SA the roles needed
310
+ // for telemetry to flow:
311
+ // - roles/logging.logWriter: container stdout → Cloud Logging
312
+ // - roles/monitoring.metricWriter: CPU/memory metrics → autoscaler
313
+ // The default Compute Engine SA gets these implicitly; our custom
314
+ // runSa needs them explicit. Without these, logs vanish silently and
315
+ // autoscaling decisions degrade. Project-scoped (telemetry endpoints
316
+ // are project-level resources).
317
+ new gcp.projects.IAMMember(
318
+ `${namePrefix}-run-log-writer`,
319
+ {
320
+ project,
321
+ role: "roles/logging.logWriter",
322
+ member: pulumi.interpolate`serviceAccount:${runSa.email}`,
323
+ },
324
+ opts,
325
+ );
326
+ new gcp.projects.IAMMember(
327
+ `${namePrefix}-run-metric-writer`,
328
+ {
329
+ project,
330
+ role: "roles/monitoring.metricWriter",
331
+ member: pulumi.interpolate`serviceAccount:${runSa.email}`,
332
+ },
333
+ opts,
334
+ );
335
+
303
336
  // =========================================================================
304
337
  // Tier 1 — Firebase Hosting site
305
338
  // =========================================================================
@@ -509,9 +542,13 @@ new gcp.cloudrunv2.ServiceIamMember(
509
542
  // resource 'namespaces/<proj>/services/<gateway-svc>'
510
543
  //
511
544
  // Scope is the gateway service only (not project-wide). The admin
512
- // already has roles/run.developer on its own service via
513
- // run.serviceAccountUser earlier in this file; the gateway needs
514
- // the explicit cross-service grant.
545
+ // runs on its own Cloud Run service AS runSa via the pod-identity
546
+ // assignment below (`serviceAccount: runSa.email` on the service
547
+ // spec) — Cloud Run handles self-invocation implicitly + does NOT
548
+ // require an explicit roles/run.developer binding on the admin
549
+ // service for the runSa to act as itself. The gateway needs the
550
+ // explicit cross-service grant because the admin's runSa is a
551
+ // DIFFERENT-resource caller from the gateway's perspective.
515
552
  new gcp.cloudrunv2.ServiceIamMember(
516
553
  `${namePrefix}-gateway-viewer-for-admin-sa`,
517
554
  {