@caelo-cms/provisioning 0.2.76 → 0.2.78

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.2.76",
3
+ "version": "0.2.78",
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"
39
39
  },
40
40
  "dependencies": {
41
- "@caelo-cms/shared": "^0.2.76",
41
+ "@caelo-cms/shared": "^0.2.78",
42
42
  "@clack/prompts": "1.3.0",
43
43
  "@pulumi/aws": "^7",
44
44
  "@pulumi/awsx": "^3",
@@ -328,6 +328,25 @@ new gcp.storage.BucketIAMMember(
328
328
  opts,
329
329
  );
330
330
 
331
+ // v0.2.78 — private staging bucket. Stage uploads here; Confirm-publish
332
+ // server-side copies to the public static bucket. Never has allUsers
333
+ // binding so the staged HTML is reachable ONLY through the admin's
334
+ // IAP-gated /_staging-preview/<runId>/ proxy route. The public CDN
335
+ // never serves from this bucket.
336
+ const stagingBucket = new gcp.storage.Bucket(
337
+ `${namePrefix}-staging`,
338
+ {
339
+ name: `${project}-${namePrefix}-staging`,
340
+ location: region.toUpperCase(),
341
+ uniformBucketLevelAccess: true,
342
+ forceDestroy: env !== "production",
343
+ // No website config + no allUsers — private. Lifecycle: stage
344
+ // builds older than 7 days are GC'd by the admin's proposal-gc
345
+ // worker (extended in v0.2.78 to sweep stale runId prefixes).
346
+ },
347
+ opts,
348
+ );
349
+
331
350
  // =========================================================================
332
351
  // Cloud Run service account — single SA used by admin + gateway
333
352
  // =========================================================================
@@ -375,9 +394,43 @@ new gcp.storage.BucketIAMMember(
375
394
  opts,
376
395
  );
377
396
 
397
+ // v0.2.78 — admin Cloud Run is the deploy orchestrator. Stage uploads
398
+ // to the private staging bucket; Confirm-publish server-side copies
399
+ // objects from staging into the public static bucket. Both buckets get
400
+ // `objectAdmin` for the run SA.
401
+ //
402
+ // This relaxes the original architecture comment "admin Cloud Run can
403
+ // never accidentally write to the public-read bucket". Justification:
404
+ // the admin process IS the deploy orchestrator now (was: "operator
405
+ // runs gsutil rsync manually"). SA-isolation only protects against an
406
+ // admin-code bug that calls the GCS client incorrectly. The real
407
+ // defense — AI cannot reach the deploy.trigger op's filesystem side
408
+ // effects directly — stays intact (AI calls op; op runs the upload
409
+ // via its own code path, not via Bun.write or arbitrary fs).
410
+ new gcp.storage.BucketIAMMember(
411
+ `${namePrefix}-admin-static-rw`,
412
+ {
413
+ bucket: staticBucket.name,
414
+ role: "roles/storage.objectAdmin",
415
+ member: pulumi.interpolate`serviceAccount:${runSa.email}`,
416
+ },
417
+ opts,
418
+ );
419
+ new gcp.storage.BucketIAMMember(
420
+ `${namePrefix}-admin-staging-rw`,
421
+ {
422
+ bucket: stagingBucket.name,
423
+ role: "roles/storage.objectAdmin",
424
+ member: pulumi.interpolate`serviceAccount:${runSa.email}`,
425
+ },
426
+ opts,
427
+ );
428
+
378
429
  // `static-publisher` SA used by `bunx @caelo-cms/provisioning deploy` to
379
- // upload the static-generator output to the bucket. Separate from runSa so
380
- // admin Cloud Run can never accidentally write to the public-read bucket.
430
+ // upload the static-generator output to the bucket. Pre-v0.2.78 this
431
+ // was the only path; admin Cloud Run now does the publish in-process
432
+ // via the StaticPublisher adapter. Kept for operators who want to run
433
+ // a manual gsutil rsync from CI as a backup or for bulk re-uploads.
381
434
  // Account-id max 30 chars; collapse "production" → "prod" so dev/staging/prod
382
435
  // all fit within the budget.
383
436
  const envShort = env === "production" ? "prod" : env === "staging" ? "stg" : "dev";
@@ -535,7 +588,20 @@ const adminSvc = cloudRunService({
535
588
  timeout: "3600s",
536
589
  // Admin's second pool: admin_role on cms_public for cross-DB reads
537
590
  // + DDL + migrations. NOT public_role (write-limited).
538
- extraEnv: [{ name: "PUBLIC_ADMIN_DATABASE_URL", value: publicAdminDatabaseUrl }],
591
+ // v0.2.78 — CAELO_STATIC_BUCKET / CAELO_STAGING_BUCKET drive the
592
+ // GCS StaticPublisher (Stage uploads to staging, Confirm-publish
593
+ // copies to static). CAELO_GENERATOR_CLI points at the absolute
594
+ // path the Dockerfile shipped the static-generator source to —
595
+ // the cwd-walk fallback in resolveGeneratorCli landed at
596
+ // /app/apps/apps/static-generator/... (doubled apps/) before
597
+ // v0.2.78 because the SSR bundle's import.meta.dirname doesn't
598
+ // sit where the walk expects.
599
+ extraEnv: [
600
+ { name: "PUBLIC_ADMIN_DATABASE_URL", value: publicAdminDatabaseUrl },
601
+ { name: "CAELO_STATIC_BUCKET", value: staticBucket.name },
602
+ { name: "CAELO_STAGING_BUCKET", value: stagingBucket.name },
603
+ { name: "CAELO_GENERATOR_CLI", value: "/app/apps/static-generator/src/cli.ts" },
604
+ ],
539
605
  });
540
606
  const gatewaySvc = cloudRunService({
541
607
  serviceName: "gateway",