@caelo-cms/provisioning 0.11.9 → 0.11.11
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/dist/adapter.d.ts +6 -0
- package/dist/adapter.d.ts.map +1 -1
- package/dist/admin-domain.d.ts +10 -1
- package/dist/admin-domain.d.ts.map +1 -1
- package/dist/admin-domain.js +13 -3
- package/dist/admin-domain.js.map +1 -1
- package/dist/adopt-existing.d.ts +12 -3
- package/dist/adopt-existing.d.ts.map +1 -1
- package/dist/adopt-existing.js +143 -0
- package/dist/adopt-existing.js.map +1 -1
- package/dist/agent/command.d.ts +23 -0
- package/dist/agent/command.d.ts.map +1 -1
- package/dist/agent/command.js +32 -9
- package/dist/agent/command.js.map +1 -1
- package/dist/agent/run.d.ts +2 -0
- package/dist/agent/run.d.ts.map +1 -1
- package/dist/agent/run.js +5 -1
- package/dist/agent/run.js.map +1 -1
- package/dist/agent-db.js +69 -10
- package/dist/aws-task-roles.d.ts +4 -2
- package/dist/aws-task-roles.d.ts.map +1 -1
- package/dist/aws-task-roles.js +4 -1
- package/dist/aws-task-roles.js.map +1 -1
- package/dist/caddy.d.ts +21 -4
- package/dist/caddy.d.ts.map +1 -1
- package/dist/caddy.js +27 -3
- package/dist/caddy.js.map +1 -1
- package/dist/cli-routing.js +1 -1
- package/dist/cli-routing.js.map +1 -1
- package/dist/cli.js +68 -7
- package/dist/cli.js.map +1 -1
- package/dist/cloudsql-tls.d.ts +93 -0
- package/dist/cloudsql-tls.d.ts.map +1 -0
- package/dist/cloudsql-tls.js +295 -0
- package/dist/cloudsql-tls.js.map +1 -0
- package/dist/compose.d.ts +43 -6
- package/dist/compose.d.ts.map +1 -1
- package/dist/compose.js +148 -23
- package/dist/compose.js.map +1 -1
- package/dist/converge-checkpoint.d.ts +1 -0
- package/dist/converge-checkpoint.d.ts.map +1 -1
- package/dist/converge-checkpoint.js +8 -1
- package/dist/converge-checkpoint.js.map +1 -1
- package/dist/converge-db-trust.d.ts +44 -0
- package/dist/converge-db-trust.d.ts.map +1 -0
- package/dist/converge-db-trust.js +158 -0
- package/dist/converge-db-trust.js.map +1 -0
- package/dist/converge-job-gcp.d.ts +5 -2
- package/dist/converge-job-gcp.d.ts.map +1 -1
- package/dist/converge-job-gcp.js +11 -13
- package/dist/converge-job-gcp.js.map +1 -1
- package/dist/converge-job-preflight.d.ts.map +1 -1
- package/dist/converge-job-preflight.js +8 -0
- package/dist/converge-job-preflight.js.map +1 -1
- package/dist/converge-job.d.ts +17 -0
- package/dist/converge-job.d.ts.map +1 -1
- package/dist/converge-job.js +12 -0
- package/dist/converge-job.js.map +1 -1
- package/dist/deployed-release.d.ts +4 -1
- package/dist/deployed-release.d.ts.map +1 -1
- package/dist/deployed-release.js +18 -8
- package/dist/deployed-release.js.map +1 -1
- package/dist/gcp-names.d.ts +34 -0
- package/dist/gcp-names.d.ts.map +1 -1
- package/dist/gcp-names.js +45 -0
- package/dist/gcp-names.js.map +1 -1
- package/dist/gcp-url-map.d.ts.map +1 -1
- package/dist/gcp-url-map.js +6 -2
- package/dist/gcp-url-map.js.map +1 -1
- package/dist/index.d.ts +3 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/infra-contract.d.ts +1 -1
- package/dist/infra-contract.d.ts.map +1 -1
- package/dist/infra-contract.js +45 -1
- package/dist/infra-contract.js.map +1 -1
- package/dist/install-job.d.ts +4 -2
- package/dist/install-job.d.ts.map +1 -1
- package/dist/install-job.js +4 -1
- package/dist/install-job.js.map +1 -1
- package/dist/install-state.d.ts +13 -0
- package/dist/install-state.d.ts.map +1 -1
- package/dist/install-state.js +17 -4
- package/dist/install-state.js.map +1 -1
- package/dist/lifecycle.d.ts.map +1 -1
- package/dist/lifecycle.js +20 -8
- package/dist/lifecycle.js.map +1 -1
- package/dist/mcp-auth-proxy-setup.d.ts +48 -0
- package/dist/mcp-auth-proxy-setup.d.ts.map +1 -0
- package/dist/mcp-auth-proxy-setup.js +280 -0
- package/dist/mcp-auth-proxy-setup.js.map +1 -0
- package/dist/migration-runner.d.ts.map +1 -1
- package/dist/migration-runner.js +8 -3
- package/dist/migration-runner.js.map +1 -1
- package/dist/migration-targets.d.ts +11 -0
- package/dist/migration-targets.d.ts.map +1 -0
- package/dist/migration-targets.js +12 -0
- package/dist/migration-targets.js.map +1 -0
- package/dist/operator-access.d.ts +10 -0
- package/dist/operator-access.d.ts.map +1 -1
- package/dist/operator-access.js +5 -0
- package/dist/operator-access.js.map +1 -1
- package/dist/pool-budget-stack.d.ts +13 -1
- package/dist/pool-budget-stack.d.ts.map +1 -1
- package/dist/pool-budget-stack.js +11 -1
- package/dist/pool-budget-stack.js.map +1 -1
- package/dist/public-role-isolation.d.ts +7 -3
- package/dist/public-role-isolation.d.ts.map +1 -1
- package/dist/public-role-isolation.js +25 -13
- package/dist/public-role-isolation.js.map +1 -1
- package/dist/release-contract.d.ts.map +1 -1
- package/dist/release-contract.js +3 -0
- package/dist/release-contract.js.map +1 -1
- package/dist/release-manifest.d.ts +25 -1
- package/dist/release-manifest.d.ts.map +1 -1
- package/dist/release-manifest.js +36 -3
- package/dist/release-manifest.js.map +1 -1
- package/dist/remote-mcp.d.ts +55 -18
- package/dist/remote-mcp.d.ts.map +1 -1
- package/dist/remote-mcp.js +307 -77
- package/dist/remote-mcp.js.map +1 -1
- package/dist/runtime-secrets.d.ts +11 -0
- package/dist/runtime-secrets.d.ts.map +1 -1
- package/dist/runtime-secrets.js +40 -25
- package/dist/runtime-secrets.js.map +1 -1
- package/dist/scheduler-setup.d.ts +75 -0
- package/dist/scheduler-setup.d.ts.map +1 -0
- package/dist/scheduler-setup.js +331 -0
- package/dist/scheduler-setup.js.map +1 -0
- package/dist/seal-key-rotation.d.ts +7 -7
- package/dist/seal-key-rotation.d.ts.map +1 -1
- package/dist/seal-key-rotation.js +22 -22
- package/dist/seal-key-rotation.js.map +1 -1
- package/dist/self-hosted-db-tls.d.ts +37 -0
- package/dist/self-hosted-db-tls.d.ts.map +1 -0
- package/dist/self-hosted-db-tls.js +220 -0
- package/dist/self-hosted-db-tls.js.map +1 -0
- package/dist/self-hosted-release.d.ts +3 -1
- package/dist/self-hosted-release.d.ts.map +1 -1
- package/dist/self-hosted-release.js +33 -9
- package/dist/self-hosted-release.js.map +1 -1
- package/dist/self-hosted-roles.d.ts +43 -2
- package/dist/self-hosted-roles.d.ts.map +1 -1
- package/dist/self-hosted-roles.js +87 -1
- package/dist/self-hosted-roles.js.map +1 -1
- package/dist/self-hosted-scheduler-interval.d.ts +21 -0
- package/dist/self-hosted-scheduler-interval.d.ts.map +1 -0
- package/dist/self-hosted-scheduler-interval.js +68 -0
- package/dist/self-hosted-scheduler-interval.js.map +1 -0
- package/dist/self-hosted-start.d.ts +0 -21
- package/dist/self-hosted-start.d.ts.map +1 -1
- package/dist/self-hosted-start.js +39 -1
- package/dist/self-hosted-start.js.map +1 -1
- package/dist/self-hosted-upgrade.d.ts +19 -7
- package/dist/self-hosted-upgrade.d.ts.map +1 -1
- package/dist/self-hosted-upgrade.js +78 -25
- package/dist/self-hosted-upgrade.js.map +1 -1
- package/dist/stack-contract.d.ts +234 -28
- package/dist/stack-contract.d.ts.map +1 -1
- package/dist/stack-contract.js +397 -39
- package/dist/stack-contract.js.map +1 -1
- package/dist/stack-converge.d.ts +31 -0
- package/dist/stack-converge.d.ts.map +1 -1
- package/dist/stack-converge.js +71 -30
- package/dist/stack-converge.js.map +1 -1
- package/dist/staging-database.d.ts +22 -0
- package/dist/staging-database.d.ts.map +1 -0
- package/dist/staging-database.js +117 -0
- package/dist/staging-database.js.map +1 -0
- package/dist/upgrade/app-roll.d.ts +25 -4
- package/dist/upgrade/app-roll.d.ts.map +1 -1
- package/dist/upgrade/app-roll.js +63 -17
- package/dist/upgrade/app-roll.js.map +1 -1
- package/dist/upgrade/converge.d.ts +5 -0
- package/dist/upgrade/converge.d.ts.map +1 -1
- package/dist/upgrade/converge.js +207 -20
- package/dist/upgrade/converge.js.map +1 -1
- package/dist/upgrade/health.d.ts +31 -7
- package/dist/upgrade/health.d.ts.map +1 -1
- package/dist/upgrade/health.js +41 -17
- package/dist/upgrade/health.js.map +1 -1
- package/dist/upgrade/image-forms.d.ts +1 -1
- package/dist/upgrade/image-forms.d.ts.map +1 -1
- package/dist/upgrade/image-forms.js +24 -7
- package/dist/upgrade/image-forms.js.map +1 -1
- package/dist/upgrade/release-images.d.ts +12 -3
- package/dist/upgrade/release-images.d.ts.map +1 -1
- package/dist/upgrade/release-images.js +13 -4
- package/dist/upgrade/release-images.js.map +1 -1
- package/dist/upgrade/upgrade.d.ts +19 -3
- package/dist/upgrade/upgrade.d.ts.map +1 -1
- package/dist/upgrade/upgrade.js +37 -2
- package/dist/upgrade/upgrade.js.map +1 -1
- package/dist/upgrader-gcp.d.ts +13 -0
- package/dist/upgrader-gcp.d.ts.map +1 -1
- package/dist/upgrader-gcp.js +50 -1
- package/dist/upgrader-gcp.js.map +1 -1
- package/dist/upgrader-iam-preflight.d.ts.map +1 -1
- package/dist/upgrader-iam-preflight.js +6 -1
- package/dist/upgrader-iam-preflight.js.map +1 -1
- package/dist/upgrader-job.d.ts +1 -1
- package/dist/upgrader-job.d.ts.map +1 -1
- package/dist/upgrader-job.js +20 -14
- package/dist/upgrader-job.js.map +1 -1
- package/dist/upgrader-records.d.ts +1 -1
- package/dist/upgrader-records.d.ts.map +1 -1
- package/dist/upgrader-records.js +26 -7
- package/dist/upgrader-records.js.map +1 -1
- package/dist/upgrader-setup.d.ts.map +1 -1
- package/dist/upgrader-setup.js +10 -5
- package/dist/upgrader-setup.js.map +1 -1
- package/dist/wizard.d.ts +7 -0
- package/dist/wizard.d.ts.map +1 -1
- package/dist/wizard.js +11 -0
- package/dist/wizard.js.map +1 -1
- package/dist/wizards/gcp-cost.d.ts +31 -0
- package/dist/wizards/gcp-cost.d.ts.map +1 -1
- package/dist/wizards/gcp-cost.js +103 -18
- package/dist/wizards/gcp-cost.js.map +1 -1
- package/dist/wizards/gcp.d.ts.map +1 -1
- package/dist/wizards/gcp.js +129 -27
- package/dist/wizards/gcp.js.map +1 -1
- package/package.json +2 -2
- package/src/adapter.ts +6 -0
- package/src/admin-domain.test.ts +9 -8
- package/src/admin-domain.ts +13 -3
- package/src/admin-memory.test.ts +2 -1
- package/src/adopt-existing.test.ts +85 -0
- package/src/adopt-existing.ts +141 -3
- package/src/agent/command.test.ts +49 -1
- package/src/agent/command.ts +39 -9
- package/src/agent/run.test.ts +19 -0
- package/src/agent/run.ts +11 -2
- package/src/aws-task-roles.ts +5 -2
- package/src/caddy-redirects.docker.test.ts +2 -0
- package/src/caddy-redirects.test.ts +4 -1
- package/src/caddy-validate.test.ts +7 -1
- package/src/caddy.test.ts +39 -1
- package/src/caddy.ts +36 -4
- package/src/checkpoint-secrets.test.ts +14 -6
- package/src/cli-routing.test.ts +1 -1
- package/src/cli-routing.ts +1 -1
- package/src/cli.ts +71 -5
- package/src/cloudsql-tls.test.ts +255 -0
- package/src/cloudsql-tls.ts +359 -0
- package/src/compose.ts +193 -24
- package/src/converge-checkpoint.ts +8 -1
- package/src/converge-db-trust.test.ts +237 -0
- package/src/converge-db-trust.ts +212 -0
- package/src/converge-job-gcp.ts +11 -12
- package/src/converge-job-preflight.test.ts +5 -0
- package/src/converge-job-preflight.ts +8 -0
- package/src/converge-job.test.ts +70 -0
- package/src/converge-job.ts +32 -0
- package/src/database-tls-builders.test.ts +56 -0
- package/src/deployed-release.test.ts +107 -40
- package/src/deployed-release.ts +30 -9
- package/src/gateway-env-isolation.test.ts +50 -12
- package/src/gcp-names.ts +56 -0
- package/src/gcp-url-map.test.ts +6 -8
- package/src/gcp-url-map.ts +6 -2
- package/src/index.test.ts +10 -4
- package/src/index.ts +7 -0
- package/src/infra-contract.test.ts +7 -3
- package/src/infra-contract.ts +45 -1
- package/src/install-job.ts +4 -1
- package/src/install-state.ts +33 -4
- package/src/lifecycle.ts +23 -11
- package/src/mcp-auth-proxy-setup.test.ts +305 -0
- package/src/mcp-auth-proxy-setup.ts +374 -0
- package/src/mcp-iap-stacks.test.ts +1 -1
- package/src/media-volume.test.ts +1 -1
- package/src/migration-runner.ts +14 -9
- package/src/migration-targets.ts +12 -0
- package/src/operator-access-stacks.test.ts +1 -1
- package/src/operator-access.test.ts +20 -4
- package/src/operator-access.ts +14 -0
- package/src/pool-budget-stack.ts +15 -0
- package/src/pool-budget-stacks.test.ts +39 -14
- package/src/public-role-isolation.test.ts +18 -8
- package/src/public-role-isolation.ts +31 -17
- package/src/release-contract.ts +3 -0
- package/src/release-manifest.test.ts +25 -0
- package/src/release-manifest.ts +59 -4
- package/src/remote-mcp-stack-config.test.ts +19 -9
- package/src/remote-mcp.test.ts +252 -57
- package/src/remote-mcp.ts +336 -77
- package/src/runtime-secrets.test.ts +37 -14
- package/src/runtime-secrets.ts +87 -27
- package/src/scheduler-setup.test.ts +201 -0
- package/src/scheduler-setup.ts +375 -0
- package/src/seal-key-rotation.test.ts +2 -2
- package/src/seal-key-rotation.ts +25 -25
- package/src/self-hosted-backup.test.ts +1 -0
- package/src/self-hosted-compose.test.ts +180 -6
- package/src/self-hosted-db-tls.test.ts +111 -0
- package/src/self-hosted-db-tls.ts +287 -0
- package/src/self-hosted-release-resolve.test.ts +71 -0
- package/src/self-hosted-release.ts +41 -11
- package/src/self-hosted-roles.integration.test.ts +2 -0
- package/src/self-hosted-roles.test.ts +87 -3
- package/src/self-hosted-roles.ts +101 -3
- package/src/self-hosted-scheduler-interval.test.ts +66 -0
- package/src/self-hosted-scheduler-interval.ts +79 -0
- package/src/self-hosted-start.test.ts +35 -2
- package/src/self-hosted-start.ts +20 -2
- package/src/self-hosted-upgrade.test.ts +104 -14
- package/src/self-hosted-upgrade.ts +123 -31
- package/src/stack-contract-parity.test.ts +79 -12
- package/src/stack-contract.test.ts +199 -31
- package/src/stack-contract.ts +472 -45
- package/src/stack-converge-secrets.test.ts +28 -13
- package/src/stack-converge.test.ts +203 -28
- package/src/stack-converge.ts +92 -30
- package/src/staging-database.test.ts +114 -0
- package/src/staging-database.ts +148 -0
- package/src/state-store.test.ts +2 -1
- package/src/upgrade/app-roll.ts +97 -19
- package/src/upgrade/converge.ts +249 -19
- package/src/upgrade/health.test.ts +63 -25
- package/src/upgrade/health.ts +54 -22
- package/src/upgrade/image-forms.test.ts +34 -0
- package/src/upgrade/image-forms.ts +23 -8
- package/src/upgrade/release-images.test.ts +5 -1
- package/src/upgrade/release-images.ts +22 -6
- package/src/upgrade/upgrade.test.ts +438 -46
- package/src/upgrade/upgrade.ts +64 -6
- package/src/upgrader-gcp.ts +66 -3
- package/src/upgrader-iam-preflight.test.ts +19 -0
- package/src/upgrader-iam-preflight.ts +6 -0
- package/src/upgrader-job.test.ts +101 -27
- package/src/upgrader-job.ts +27 -14
- package/src/upgrader-records.test.ts +97 -12
- package/src/upgrader-records.ts +28 -11
- package/src/upgrader-setup.test.ts +2 -0
- package/src/upgrader-setup.ts +10 -4
- package/src/wizard.ts +19 -0
- package/src/wizards/gcp-cost.test.ts +103 -0
- package/src/wizards/gcp-cost.ts +125 -18
- package/src/wizards/gcp.ts +164 -22
- package/stacks/aws/Pulumi.yaml +1 -1
- package/stacks/aws/index.ts +72 -14
- package/stacks/azure/index.ts +108 -21
- package/stacks/gcp/Pulumi.yaml +1 -1
- package/stacks/gcp/README.md +1 -0
- package/stacks/gcp/index.ts +133 -24
- package/stacks/gcp-firebase/Pulumi.yaml +1 -1
- package/stacks/gcp-firebase/index.ts +212 -34
- package/stacks/self-hosted/Pulumi.yaml +3 -0
- package/stacks/self-hosted/README.md +6 -1
- package/stacks/self-hosted/index.ts +40 -5
|
@@ -7,8 +7,10 @@
|
|
|
7
7
|
* - Static site: Firebase Hosting site (no LB, no BackendBucket)
|
|
8
8
|
* - Admin: Cloud Run + custom domain mapping (admin.<domain>)
|
|
9
9
|
* + IAP-on-Cloud-Run (no LB IAP)
|
|
10
|
-
* - Gateway: Cloud Run
|
|
11
|
-
* no Cloud Armor)
|
|
10
|
+
* - Gateway: Cloud Run `caelo-<env>-data-gateway` + Firebase
|
|
11
|
+
* Hosting `rewrites` (no LB, no Cloud Armor)
|
|
12
|
+
* - MCP-Auth-Proxy: Cloud Run on its own run.app URL (#1328; the
|
|
13
|
+
* service that was the gateway, E13 a of #763)
|
|
12
14
|
* - Database: Cloud SQL Postgres on private VPC IP only (same
|
|
13
15
|
* as `gcp/`)
|
|
14
16
|
* - Workers: share admin Cloud Run (same as `gcp/`)
|
|
@@ -35,11 +37,14 @@ import {
|
|
|
35
37
|
gcpBucketName,
|
|
36
38
|
gcpSecretId,
|
|
37
39
|
MCP_IAP_SERVICE_ACCOUNT_ID,
|
|
40
|
+
mcpAuthProxyServiceAccountId,
|
|
38
41
|
runServiceAccountId,
|
|
39
42
|
} from "../../dist/gcp-names.js";
|
|
40
43
|
import {
|
|
44
|
+
DEFAULT_ADMIN_MAX_INSTANCES,
|
|
41
45
|
DEFAULT_GATEWAY_MAX_INSTANCES,
|
|
42
46
|
DEFAULT_MAX_CONNECTIONS,
|
|
47
|
+
DEFAULT_STAGING_GATEWAY_MAX_INSTANCES,
|
|
43
48
|
stackPoolBudget,
|
|
44
49
|
} from "../../dist/pool-budget-stack.js";
|
|
45
50
|
import {
|
|
@@ -48,17 +53,27 @@ import {
|
|
|
48
53
|
adminMediaVolume,
|
|
49
54
|
adminMediaVolumeTemplate,
|
|
50
55
|
CLOUD_RUN_IGNORE_CHANGES,
|
|
56
|
+
CLOUD_SQL_PSA_CONNECTION_TYPE,
|
|
57
|
+
CLOUD_SQL_SERVER_CA_MODE,
|
|
58
|
+
CLOUD_SQL_SSL_MODE,
|
|
51
59
|
type CloudRunEnvVar,
|
|
52
60
|
type CloudRunSlug,
|
|
61
|
+
cloudSqlDnsZoneName,
|
|
62
|
+
cloudSqlPrivateDnsName,
|
|
63
|
+
DEFAULT_MCP_AUTH_PROXY_MAX_INSTANCES,
|
|
53
64
|
databaseUrls,
|
|
54
65
|
defaultStagingHost,
|
|
55
|
-
|
|
66
|
+
fixedCloudRunServiceName,
|
|
56
67
|
gatewayEnvContract,
|
|
68
|
+
INTERIM_FORWARD_ORIGIN_ENV,
|
|
69
|
+
MCP_AUTH_PROXY_TIMEOUT_SECONDS,
|
|
70
|
+
mcpAuthProxyEnvContract,
|
|
57
71
|
REMOTE_MCP_IDENTITY_CONFIG,
|
|
58
72
|
type RuntimeSecret,
|
|
59
73
|
remoteMcpIdentityStackEnv,
|
|
60
74
|
runtimeSecretBindings,
|
|
61
75
|
SERVICE_CPU_IDLE,
|
|
76
|
+
SERVICE_VPC_EGRESS,
|
|
62
77
|
secretFileVolumesTemplate,
|
|
63
78
|
} from "../../dist/stack-contract.js";
|
|
64
79
|
|
|
@@ -96,7 +111,10 @@ const adminMinInstances = Number.parseInt(cfg.get("adminMinInstances") ?? "0", 1
|
|
|
96
111
|
// #553 — the admin also runs the Lighthouse quality audit; same knob and
|
|
97
112
|
// default on every adapter (stack-contract.ts ADMIN_MEMORY_DEFAULT).
|
|
98
113
|
const adminMemory = cfg.get("adminMemory") ?? ADMIN_MEMORY_DEFAULT;
|
|
99
|
-
const adminMaxInstances = Number.parseInt(
|
|
114
|
+
const adminMaxInstances = Number.parseInt(
|
|
115
|
+
cfg.get("adminMaxInstances") ?? String(DEFAULT_ADMIN_MAX_INSTANCES),
|
|
116
|
+
10,
|
|
117
|
+
);
|
|
100
118
|
const gatewayMinInstances = Number.parseInt(cfg.get("gatewayMinInstances") ?? "0", 10);
|
|
101
119
|
// #1254 — part of the connection budget (stacks/gcp has the long comment).
|
|
102
120
|
const gatewayMaxInstances = Number.parseInt(
|
|
@@ -112,6 +130,8 @@ const poolBudget = stackPoolBudget("gcp-firebase", {
|
|
|
112
130
|
maxConnections,
|
|
113
131
|
adminMaxInstances,
|
|
114
132
|
gatewayMaxInstances,
|
|
133
|
+
// #1329 — the staging gateway (P3b-2) is budgeted from this release on.
|
|
134
|
+
stagingGatewayMaxInstances: DEFAULT_STAGING_GATEWAY_MAX_INSTANCES,
|
|
115
135
|
});
|
|
116
136
|
const backupRetentionDays = Number.parseInt(cfg.get("backupRetentionDays") ?? "7", 10);
|
|
117
137
|
const iapAllowlistRaw = cfg.get("iapAllowlist");
|
|
@@ -249,6 +269,8 @@ const runtimeSecretIds: Record<RuntimeSecret, pulumi.Input<string>> = {
|
|
|
249
269
|
"tool-approval-secret": gcpSecretId(env, "tool-approval-secret"),
|
|
250
270
|
"staging-gate-secret": gcpSecretId(env, "staging-gate-secret"),
|
|
251
271
|
"public-role-password": gcpSecretId(env, "public-role-password"),
|
|
272
|
+
// #1329 — the staging gateway's login, never the live one's.
|
|
273
|
+
"public-staging-role-password": gcpSecretId(env, "public-staging-role-password"),
|
|
252
274
|
// #914 / #806 — the Owner-set Google sign-in client (a placeholder until
|
|
253
275
|
// then; IAP login, IAP programmatic access and the remote-MCP broker) and
|
|
254
276
|
// the broker's seal key, both CLI-generated.
|
|
@@ -265,6 +287,14 @@ const publicRolePassword = pulumi.secret(
|
|
|
265
287
|
{ provider: gcpProvider },
|
|
266
288
|
).secretData,
|
|
267
289
|
);
|
|
290
|
+
// #1329 — public_staging_role's own password (CLI-generated like the one
|
|
291
|
+
// above): the staging gateway's only login, on cms_public_staging only.
|
|
292
|
+
const publicStagingRolePassword = pulumi.secret(
|
|
293
|
+
gcp.secretmanager.getSecretVersionOutput(
|
|
294
|
+
{ project, secret: gcpSecretId(env, "public-staging-role-password") },
|
|
295
|
+
{ provider: gcpProvider },
|
|
296
|
+
).secretData,
|
|
297
|
+
);
|
|
268
298
|
|
|
269
299
|
// =========================================================================
|
|
270
300
|
// Tier 4 — Cloud SQL Postgres (private IP only; HA configurable)
|
|
@@ -293,6 +323,11 @@ const sqlInstance = new gcp.sql.DatabaseInstance(
|
|
|
293
323
|
ipv4Enabled: false,
|
|
294
324
|
privateNetwork: network.id,
|
|
295
325
|
enablePrivatePathForGoogleCloudServices: true,
|
|
326
|
+
// #1338 — the shared CA names the instance's DNS name in its server
|
|
327
|
+
// certificate, so every client verifies it (sslmode=verify-full);
|
|
328
|
+
// unencrypted connections are refused outright.
|
|
329
|
+
serverCaMode: CLOUD_SQL_SERVER_CA_MODE,
|
|
330
|
+
sslMode: CLOUD_SQL_SSL_MODE,
|
|
296
331
|
},
|
|
297
332
|
databaseFlags: [
|
|
298
333
|
{ name: "max_connections", value: maxConnections.toString() },
|
|
@@ -316,6 +351,13 @@ const cmsPublicDb = new gcp.sql.Database(
|
|
|
316
351
|
{ instance: sqlInstance.name, name: "cms_public" },
|
|
317
352
|
opts,
|
|
318
353
|
);
|
|
354
|
+
// #1329 — staging's own data: the twin of cms_public on the same instance
|
|
355
|
+
// (no extra cost), reached by the staging gateway only.
|
|
356
|
+
const cmsPublicStagingDb = new gcp.sql.Database(
|
|
357
|
+
`${namePrefix}-cms-public-staging`,
|
|
358
|
+
{ instance: sqlInstance.name, name: "cms_public_staging" },
|
|
359
|
+
opts,
|
|
360
|
+
);
|
|
319
361
|
|
|
320
362
|
const pgAdminUser = new gcp.sql.User(
|
|
321
363
|
`${namePrefix}-admin-user`,
|
|
@@ -334,10 +376,58 @@ new gcp.sql.User(
|
|
|
334
376
|
{ instance: sqlInstance.name, name: "public_role", password: publicRolePassword },
|
|
335
377
|
opts,
|
|
336
378
|
);
|
|
379
|
+
// #1329 — the staging gateway's login: an Admin API user like public_role,
|
|
380
|
+
// so the same membership revoke (public-role-isolation.ts) and the same
|
|
381
|
+
// audit apply; migration 0530 gives it CONNECT on cms_public_staging only.
|
|
382
|
+
new gcp.sql.User(
|
|
383
|
+
`${namePrefix}-public-staging-user`,
|
|
384
|
+
{ instance: sqlInstance.name, name: "public_staging_role", password: publicStagingRolePassword },
|
|
385
|
+
opts,
|
|
386
|
+
);
|
|
387
|
+
|
|
388
|
+
// #1338 — the instance's DNS name, resolved to its private IP by a private
|
|
389
|
+
// zone in the VPC (Cloud SQL assigns the name, it does not resolve it).
|
|
390
|
+
// Every client dials this name with sslmode=verify-full; the certificate
|
|
391
|
+
// names no IP. `cms-provision upgrade` converges the same zone and record
|
|
392
|
+
// through gcloud (cloudsql-tls.ts); the installer adopts them (#1079).
|
|
393
|
+
const sqlDnsName = sqlInstance.dnsNames.apply((names) => {
|
|
394
|
+
const name = cloudSqlPrivateDnsName(names);
|
|
395
|
+
if (!name) {
|
|
396
|
+
throw new Error(
|
|
397
|
+
`Cloud SQL instance has no ${CLOUD_SQL_PSA_CONNECTION_TYPE} DNS name; it needs the shared server CA (${CLOUD_SQL_SERVER_CA_MODE}) for verify-full connections (#1338)`,
|
|
398
|
+
);
|
|
399
|
+
}
|
|
400
|
+
return name;
|
|
401
|
+
});
|
|
402
|
+
const sqlDnsZone = new gcp.dns.ManagedZone(
|
|
403
|
+
`${namePrefix}-pg-dns`,
|
|
404
|
+
{
|
|
405
|
+
name: cloudSqlDnsZoneName(env),
|
|
406
|
+
dnsName: pulumi.interpolate`${sqlDnsName}.`,
|
|
407
|
+
visibility: "private",
|
|
408
|
+
privateVisibilityConfig: { networks: [{ networkUrl: network.id }] },
|
|
409
|
+
description: `Caelo ${env}: the Cloud SQL instance's DNS name inside the VPC (#1338)`,
|
|
410
|
+
},
|
|
411
|
+
opts,
|
|
412
|
+
);
|
|
413
|
+
const sqlDnsRecord = new gcp.dns.RecordSet(
|
|
414
|
+
`${namePrefix}-pg-dns-a`,
|
|
415
|
+
{
|
|
416
|
+
// Literal names, not the zone's outputs: the installer can then adopt
|
|
417
|
+
// a record an upgrade created (adopt-existing.ts) from the preview.
|
|
418
|
+
managedZone: cloudSqlDnsZoneName(env),
|
|
419
|
+
name: pulumi.interpolate`${sqlDnsName}.`,
|
|
420
|
+
type: "A",
|
|
421
|
+
ttl: 300,
|
|
422
|
+
rrdatas: [sqlInstance.privateIpAddress],
|
|
423
|
+
},
|
|
424
|
+
{ ...opts, dependsOn: [sqlDnsZone] },
|
|
425
|
+
);
|
|
337
426
|
|
|
338
427
|
// Password-less connection URLs (stack-contract.ts databaseUrls); the
|
|
339
|
-
// services get the password as a Secret Manager reference.
|
|
340
|
-
|
|
428
|
+
// services get the password as a Secret Manager reference. Tied to the
|
|
429
|
+
// record so no revision boots before its database host resolves.
|
|
430
|
+
const dbUrls = pulumi.all([sqlDnsName, sqlDnsRecord.id]).apply(([host]) => databaseUrls(host));
|
|
341
431
|
|
|
342
432
|
// =========================================================================
|
|
343
433
|
// Media bucket — private, signed-URL access only (same as `gcp` stack)
|
|
@@ -380,9 +470,22 @@ const gatewaySa = new gcp.serviceaccount.Account(
|
|
|
380
470
|
},
|
|
381
471
|
opts,
|
|
382
472
|
);
|
|
473
|
+
// #1328 — the MCP-Auth-Proxy's own SA: secretAccessor on its two secrets
|
|
474
|
+
// below and nothing else — no project role, not even the log writer (Cloud
|
|
475
|
+
// Run ships a container's stdout to Cloud Logging without one).
|
|
476
|
+
const mcpAuthProxySa = new gcp.serviceaccount.Account(
|
|
477
|
+
`${namePrefix}-mcp-auth-proxy-sa`,
|
|
478
|
+
{
|
|
479
|
+
accountId: mcpAuthProxyServiceAccountId(env),
|
|
480
|
+
displayName: `Caelo ${env} MCP-Auth-Proxy`,
|
|
481
|
+
createIgnoreAlreadyExists: true,
|
|
482
|
+
},
|
|
483
|
+
opts,
|
|
484
|
+
);
|
|
383
485
|
const serviceSa: Record<CloudRunSlug, gcp.serviceaccount.Account> = {
|
|
384
486
|
admin: runSa,
|
|
385
487
|
gateway: gatewaySa,
|
|
488
|
+
"mcp-auth-proxy": mcpAuthProxySa,
|
|
386
489
|
};
|
|
387
490
|
|
|
388
491
|
// secretAccessor for each service's SA on exactly the secrets its env reads.
|
|
@@ -391,6 +494,7 @@ const serviceSa: Record<CloudRunSlug, gcp.serviceaccount.Account> = {
|
|
|
391
494
|
const secretBindings: Record<CloudRunSlug, gcp.secretmanager.SecretIamMember[]> = {
|
|
392
495
|
admin: [],
|
|
393
496
|
gateway: [],
|
|
497
|
+
"mcp-auth-proxy": [],
|
|
394
498
|
};
|
|
395
499
|
for (const binding of runtimeSecretBindings()) {
|
|
396
500
|
secretBindings[binding.service].push(
|
|
@@ -533,11 +637,19 @@ const firebaseCustomDomain = new gcp.firebase.HostingCustomDomain(
|
|
|
533
637
|
);
|
|
534
638
|
|
|
535
639
|
// =========================================================================
|
|
536
|
-
// Cloud Run services — admin
|
|
640
|
+
// Cloud Run services — admin, data gateway, MCP-Auth-Proxy
|
|
537
641
|
// =========================================================================
|
|
538
642
|
|
|
539
643
|
interface CloudRunArgs {
|
|
540
|
-
readonly serviceName:
|
|
644
|
+
readonly serviceName: CloudRunSlug;
|
|
645
|
+
/**
|
|
646
|
+
* The Pulumi resource name, when it is not `${namePrefix}-<serviceName>`.
|
|
647
|
+
* #1328 E13 a: the proxy keeps the logical name the gateway had, so Pulumi
|
|
648
|
+
* keeps the same resource and its auto-generated service name — an alias
|
|
649
|
+
* under a new name would let the provider pick a new name, and a new
|
|
650
|
+
* service means a new run.app URL.
|
|
651
|
+
*/
|
|
652
|
+
readonly resourceName?: string;
|
|
541
653
|
readonly minInstances: number;
|
|
542
654
|
readonly maxInstances: number;
|
|
543
655
|
readonly memory: string;
|
|
@@ -582,9 +694,13 @@ function imageRef(service: string): string {
|
|
|
582
694
|
const remoteMcpEnv = remoteMcpIdentityStackEnv(cfg.get(REMOTE_MCP_IDENTITY_CONFIG));
|
|
583
695
|
|
|
584
696
|
function cloudRunService(args: CloudRunArgs): gcp.cloudrunv2.Service {
|
|
697
|
+
// #1328 — the data gateway has a fixed name: the converge creates it on
|
|
698
|
+
// installs whose stack predates it, and the installer adopts that one.
|
|
699
|
+
const fixedName = fixedCloudRunServiceName("gcp-firebase", env, args.serviceName);
|
|
585
700
|
return new gcp.cloudrunv2.Service(
|
|
586
|
-
`${namePrefix}-${args.serviceName}`,
|
|
701
|
+
args.resourceName ?? `${namePrefix}-${args.serviceName}`,
|
|
587
702
|
{
|
|
703
|
+
...(fixedName ? { name: fixedName } : {}),
|
|
588
704
|
location: region,
|
|
589
705
|
ingress: args.ingress,
|
|
590
706
|
// v0.3.1 — Cloud Run native IAP. With this on, every request
|
|
@@ -604,8 +720,8 @@ function cloudRunService(args: CloudRunArgs): gcp.cloudrunv2.Service {
|
|
|
604
720
|
? { executionEnvironment: args.mediaVolume.executionEnvironment }
|
|
605
721
|
: {}),
|
|
606
722
|
// The admin's media bucket, plus each service's secret files (#806:
|
|
607
|
-
// the
|
|
608
|
-
// upgrade applies to existing installs.
|
|
723
|
+
// the MCP-Auth-Proxy's remote-MCP OAuth client) — both from the
|
|
724
|
+
// contract upgrade applies to existing installs.
|
|
609
725
|
volumes: [
|
|
610
726
|
...(args.mediaVolume?.volumes ?? []),
|
|
611
727
|
...secretFileVolumesTemplate(args.serviceName, env).volumes,
|
|
@@ -625,10 +741,16 @@ function cloudRunService(args: CloudRunArgs): gcp.cloudrunv2.Service {
|
|
|
625
741
|
},
|
|
626
742
|
},
|
|
627
743
|
],
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
744
|
+
// #1328 — the MCP-Auth-Proxy gets no route into the VPC (and so
|
|
745
|
+
// none to Cloud SQL's private IP): SERVICE_VPC_EGRESS.
|
|
746
|
+
...(SERVICE_VPC_EGRESS[args.serviceName]
|
|
747
|
+
? {
|
|
748
|
+
vpcAccess: {
|
|
749
|
+
networkInterfaces: [{ network: network.name, subnetwork: subnet.name }],
|
|
750
|
+
egress: "PRIVATE_RANGES_ONLY",
|
|
751
|
+
},
|
|
752
|
+
}
|
|
753
|
+
: {}),
|
|
632
754
|
},
|
|
633
755
|
},
|
|
634
756
|
{
|
|
@@ -639,6 +761,7 @@ function cloudRunService(args: CloudRunArgs): gcp.cloudrunv2.Service {
|
|
|
639
761
|
pgAdminUser,
|
|
640
762
|
cmsAdminDb,
|
|
641
763
|
cmsPublicDb,
|
|
764
|
+
cmsPublicStagingDb,
|
|
642
765
|
// The env contract names the media bucket as a string; keep it
|
|
643
766
|
// ordered before the services that use it.
|
|
644
767
|
mediaBucket,
|
|
@@ -666,33 +789,58 @@ const envContractInputs = {
|
|
|
666
789
|
admin: dbUrls.admin,
|
|
667
790
|
publicAdmin: dbUrls.publicAdmin,
|
|
668
791
|
public: dbUrls.public,
|
|
792
|
+
publicStagingAdmin: dbUrls.publicStagingAdmin,
|
|
793
|
+
publicStaging: dbUrls.publicStaging,
|
|
669
794
|
},
|
|
670
795
|
} as const;
|
|
671
796
|
|
|
672
|
-
//
|
|
673
|
-
// admin's env (the Firebase publisher
|
|
797
|
+
// The data gateway is provisioned first so its name/region can go into the
|
|
798
|
+
// admin's env (the Firebase publisher wires the /api/** rewrite to it).
|
|
799
|
+
// #1328 — a service of its own (`caelo-<env>-data-gateway`); before, the
|
|
800
|
+
// gateway was also the remote-MCP broker.
|
|
674
801
|
const gatewaySvc = cloudRunService({
|
|
675
802
|
serviceName: "gateway",
|
|
676
803
|
minInstances: gatewayMinInstances,
|
|
677
804
|
maxInstances: gatewayMaxInstances,
|
|
678
805
|
memory: "512Mi",
|
|
679
|
-
//
|
|
680
|
-
|
|
681
|
-
//
|
|
682
|
-
//
|
|
683
|
-
|
|
684
|
-
//
|
|
685
|
-
//
|
|
686
|
-
//
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
806
|
+
// Firebase Hosting cuts a rewritten request at 60 s anyway.
|
|
807
|
+
timeout: "300s",
|
|
808
|
+
// #650 — all traffic: `cms-provision staging-domain enable` maps the
|
|
809
|
+
// staging host onto the data gateway (Cloud Run refuses domain-mapped
|
|
810
|
+
// requests under an internal-only ingress), and it is invokable by
|
|
811
|
+
// allUsers anyway (data-gateway-public-invoker below): every /api request
|
|
812
|
+
// passes its rate limits, captcha and honeypot; staging requests pass the
|
|
813
|
+
// staging access gate.
|
|
814
|
+
ingress: "INGRESS_TRAFFIC_ALL",
|
|
815
|
+
contractEnv: [...gatewayEnvContract(envContractInputs), ...poolBudget.env.gateway],
|
|
816
|
+
});
|
|
817
|
+
|
|
818
|
+
// #1328 — the MCP-Auth-Proxy: remote MCP's sign-in broker and its stream to
|
|
819
|
+
// the IAP-protected admin, with no database, no VPC route and an account
|
|
820
|
+
// that reads two secrets. E13 (a) of #763: it is the resource that was the
|
|
821
|
+
// gateway (same logical name), so its service and run.app URL — the address every
|
|
822
|
+
// connector in claude.ai / ChatGPT holds, and the Google sign-in client's
|
|
823
|
+
// registered redirect URI — stay as they were. Always deployed at minimum
|
|
824
|
+
// zero; while remote MCP is not set up it answers a fixed 404 (E14 a).
|
|
825
|
+
const mcpAuthProxySvc = cloudRunService({
|
|
826
|
+
serviceName: "mcp-auth-proxy",
|
|
827
|
+
resourceName: `${namePrefix}-gateway`,
|
|
828
|
+
minInstances: 0,
|
|
829
|
+
maxInstances: DEFAULT_MCP_AUTH_PROXY_MAX_INSTANCES,
|
|
830
|
+
memory: "512Mi",
|
|
831
|
+
// #806 — chat turns stream for minutes (MCP_AUTH_PROXY_TIMEOUT_SECONDS);
|
|
832
|
+
// MCP clients call its run.app URL, never the Firebase rewrite (which
|
|
833
|
+
// buffers streams and cuts at 60 s).
|
|
834
|
+
timeout: `${MCP_AUTH_PROXY_TIMEOUT_SECONDS}s`,
|
|
835
|
+
// Public: MCP clients reach it directly; until #1331 the staging host's
|
|
836
|
+
// domain mapping (#650) still points at this service too, and the proxy
|
|
837
|
+
// forwards that traffic to the data gateway (INTERIM_FORWARD_ORIGIN_ENV).
|
|
691
838
|
ingress: "INGRESS_TRAFFIC_ALL",
|
|
692
839
|
contractEnv: [
|
|
693
|
-
...
|
|
694
|
-
...remoteMcpEnv
|
|
695
|
-
|
|
840
|
+
...mcpAuthProxyEnvContract({ provider: "gcp-firebase", env }),
|
|
841
|
+
...remoteMcpEnv["mcp-auth-proxy"],
|
|
842
|
+
// FIXME(issue #1331): goes with the staging cutover.
|
|
843
|
+
{ name: INTERIM_FORWARD_ORIGIN_ENV, value: gatewaySvc.uri },
|
|
696
844
|
],
|
|
697
845
|
});
|
|
698
846
|
|
|
@@ -751,7 +899,7 @@ const adminSvc = cloudRunService({
|
|
|
751
899
|
// authentication remain the real security boundary, exactly as
|
|
752
900
|
// designed.
|
|
753
901
|
new gcp.cloudrunv2.ServiceIamMember(
|
|
754
|
-
`${namePrefix}-gateway-public-invoker`,
|
|
902
|
+
`${namePrefix}-data-gateway-public-invoker`,
|
|
755
903
|
{
|
|
756
904
|
project,
|
|
757
905
|
location: region,
|
|
@@ -762,6 +910,21 @@ new gcp.cloudrunv2.ServiceIamMember(
|
|
|
762
910
|
opts,
|
|
763
911
|
);
|
|
764
912
|
|
|
913
|
+
// #1328 — the MCP-Auth-Proxy: MCP clients sign in and call it without a
|
|
914
|
+
// Google identity of their own. The binding the service had as the gateway,
|
|
915
|
+
// under its old logical name, so it never lapses.
|
|
916
|
+
new gcp.cloudrunv2.ServiceIamMember(
|
|
917
|
+
`${namePrefix}-gateway-public-invoker`,
|
|
918
|
+
{
|
|
919
|
+
project,
|
|
920
|
+
location: region,
|
|
921
|
+
name: mcpAuthProxySvc.name,
|
|
922
|
+
role: "roles/run.invoker",
|
|
923
|
+
member: "allUsers",
|
|
924
|
+
},
|
|
925
|
+
opts,
|
|
926
|
+
);
|
|
927
|
+
|
|
765
928
|
// v0.6.5 — `run.services.get` on the gateway for the admin runtime
|
|
766
929
|
// SA. Required at DEPLOY TIME (not runtime): when the static
|
|
767
930
|
// publisher creates a Firebase Hosting version with a Cloud Run
|
|
@@ -781,7 +944,7 @@ new gcp.cloudrunv2.ServiceIamMember(
|
|
|
781
944
|
// explicit cross-service grant because the admin's runSa is a
|
|
782
945
|
// DIFFERENT-resource caller from the gateway's perspective.
|
|
783
946
|
new gcp.cloudrunv2.ServiceIamMember(
|
|
784
|
-
`${namePrefix}-gateway-viewer-for-admin-sa`,
|
|
947
|
+
`${namePrefix}-data-gateway-viewer-for-admin-sa`,
|
|
785
948
|
{
|
|
786
949
|
project,
|
|
787
950
|
location: region,
|
|
@@ -1001,6 +1164,18 @@ new gcp.iap.WebCloudRunServiceIamMember(
|
|
|
1001
1164
|
// (packages/provisioning/src/capture-job-setup.ts), for the same reason as
|
|
1002
1165
|
// the audit job above.
|
|
1003
1166
|
|
|
1167
|
+
// =========================================================================
|
|
1168
|
+
// Scheduler tick — deliberately NOT here
|
|
1169
|
+
// =========================================================================
|
|
1170
|
+
//
|
|
1171
|
+
// #1140 — one Cloud Scheduler job starts a Cloud Workflows execution every
|
|
1172
|
+
// `schedulerIntervalMinutes` (1–15, default 5); the workflow signs an IAP
|
|
1173
|
+
// credential as the scheduler's own service account and POSTs the admin's
|
|
1174
|
+
// /api/internal/scheduler/tick, where the admin runs its periodic work and
|
|
1175
|
+
// plugin workers from its due-table. The CLI owns the account, its grants,
|
|
1176
|
+
// the workflow and the job (packages/provisioning/src/scheduler-setup.ts),
|
|
1177
|
+
// for the same reason as the audit job above.
|
|
1178
|
+
|
|
1004
1179
|
// =========================================================================
|
|
1005
1180
|
// Operator access — deliberately NOT here
|
|
1006
1181
|
// =========================================================================
|
|
@@ -1085,6 +1260,7 @@ const out: CloudAdapterOutputs = {
|
|
|
1085
1260
|
// Password-less — the password lives only in Secret Manager.
|
|
1086
1261
|
adminDatabaseUrl: dbUrls.admin as unknown as string,
|
|
1087
1262
|
publicDatabaseUrl: dbUrls.public as unknown as string,
|
|
1263
|
+
publicStagingDatabaseUrl: dbUrls.publicStaging as unknown as string,
|
|
1088
1264
|
mediaStorageUrl: pulumi.interpolate`gs://${mediaBucket.name}` as unknown as string,
|
|
1089
1265
|
// Firebase Hosting serves media via its own CDN; we proxy through
|
|
1090
1266
|
// the Firebase site's apex domain for canonical URLs.
|
|
@@ -1101,6 +1277,7 @@ const out: CloudAdapterOutputs = {
|
|
|
1101
1277
|
|
|
1102
1278
|
export const adminDatabaseUrlOut = out.adminDatabaseUrl;
|
|
1103
1279
|
export const publicDatabaseUrlOut = out.publicDatabaseUrl;
|
|
1280
|
+
export const publicStagingDatabaseUrlOut = out.publicStagingDatabaseUrl;
|
|
1104
1281
|
export const mediaStorageUrlOut = out.mediaStorageUrl;
|
|
1105
1282
|
export const mediaCdnBaseUrlOut = out.mediaCdnBaseUrl;
|
|
1106
1283
|
export const bootstrapUrlOut = out.bootstrapUrl;
|
|
@@ -1110,6 +1287,7 @@ export const providerOut = out.provider;
|
|
|
1110
1287
|
export const environmentOut = out.environment;
|
|
1111
1288
|
export const adminCloudRunUrlOut = adminSvc.uri;
|
|
1112
1289
|
export const gatewayCloudRunUrlOut = gatewaySvc.uri;
|
|
1290
|
+
export const mcpAuthProxyCloudRunUrlOut = mcpAuthProxySvc.uri;
|
|
1113
1291
|
export const adminDomainOut = adminDomain;
|
|
1114
1292
|
export const firebaseSiteIdOut = firebaseSiteId;
|
|
1115
1293
|
export const bootstrapTokenExpiresAtOut = tokenInfo.expiresAt;
|
|
@@ -29,3 +29,6 @@ config:
|
|
|
29
29
|
type: string
|
|
30
30
|
default: "2Gi"
|
|
31
31
|
description: Memory reserved for the admin container (it runs the Lighthouse quality audit).
|
|
32
|
+
caelo-self-hosted:schedulerIntervalMinutes:
|
|
33
|
+
type: integer
|
|
34
|
+
description: Minutes between scheduler ticks (1-15, default 5; #1140). On an install `cms-provision init` set up, `.caelo/config.json` records it and this key must match.
|
|
@@ -20,10 +20,11 @@ pulumi config set caelo-self-hosted:ownerEmail me@example.com
|
|
|
20
20
|
# verified and recorded in .caelo/release.json:
|
|
21
21
|
pulumi config set caelo-self-hosted:adminImage ghcr.io/caelo-cms/admin@sha256:<digest>
|
|
22
22
|
pulumi config set caelo-self-hosted:gatewayImage ghcr.io/caelo-cms/gateway@sha256:<digest>
|
|
23
|
+
pulumi config set caelo-self-hosted:mcpAuthProxyImage ghcr.io/caelo-cms/mcp-auth-proxy@sha256:<digest>
|
|
23
24
|
pulumi up
|
|
24
25
|
```
|
|
25
26
|
|
|
26
|
-
Pulumi mints the postgres and role passwords and the KEK, generates `docker-compose.yml` + `Caddyfile` + `bootstrap.sh` + `pending-token.json` under `.caelo/`, starts Postgres, runs the migrations and runs `docker compose up -d`. Point DNS for `<domain>`, `admin.<domain>` and `staging.<domain>` at the host. After the run:
|
|
27
|
+
Pulumi mints the postgres and role passwords and the KEK, generates `docker-compose.yml` + `Caddyfile` + `bootstrap.sh` + `pending-token.json` under `.caelo/`, starts Postgres, runs the migrations and runs `docker compose up -d`. Point DNS for `<domain>`, `admin.<domain>` and `staging.<domain>` at the host. The MCP sign-in service (`caelo-mcp-auth-proxy`) runs without a public host: remote MCP needs an identity proxy in front of the admin, which self-hosted installs do not have yet, so it only answers 404 and Caddy does not expose it. After the run:
|
|
27
28
|
|
|
28
29
|
```bash
|
|
29
30
|
pulumi stack output bootstrapUrl
|
|
@@ -32,6 +33,10 @@ pulumi stack output bootstrapUrl
|
|
|
32
33
|
|
|
33
34
|
Open that URL once Caddy has issued the cert (~30 s).
|
|
34
35
|
|
|
36
|
+
## Scheduler tick
|
|
37
|
+
|
|
38
|
+
The Compose file runs a `caelo-scheduler` container (the admin image, no database access, only the internal secret it signs with) that POSTs the admin's `/api/internal/scheduler/tick` every `schedulerIntervalMinutes` (#1140; 1–15, default 5). The admin runs its periodic work and plugin workers on that tick. Set the interval with `cms-provision init --scheduler-interval-minutes 2` (recorded in `.caelo/config.json`, which every CLI render reads) or, on an install without a `config.json`, with `pulumi config set caelo-self-hosted:schedulerIntervalMinutes 2`. The stack reads `.caelo/config.json` too, so a `pulumi up` keeps the recorded interval; a Pulumi key that disagrees with it is refused. An install from before the scheduler gets the container on its next `upgrade`, which rewrites the Compose file.
|
|
39
|
+
|
|
35
40
|
## Connection budget
|
|
36
41
|
|
|
37
42
|
The generated Compose file starts Postgres with `max_connections=100` and sizes every pool from it (#1254, docs/dev/connection-budget.md): the admin gets a cms_admin pool of 77 and a cms_public pool of 4, the gateway a pool of 2, and static builds 2 per pool.
|
|
@@ -29,11 +29,17 @@ import { generateCaddyfile, selfHostedAdminHost, selfHostedSeedDomains } from ".
|
|
|
29
29
|
import {
|
|
30
30
|
COMPOSE_ADMIN_UPSTREAM,
|
|
31
31
|
COMPOSE_GATEWAY_UPSTREAM,
|
|
32
|
+
COMPOSE_MCP_AUTH_PROXY_UPSTREAM,
|
|
32
33
|
composeProjectName,
|
|
33
34
|
generateDockerCompose,
|
|
34
35
|
SITE_OUTPUT_ROOT,
|
|
35
36
|
} from "../../dist/compose.js";
|
|
37
|
+
import { MIGRATION_TARGETS } from "../../dist/migration-targets.js";
|
|
36
38
|
import { selfHostedBootstrapScript } from "../../dist/self-hosted-roles.js";
|
|
39
|
+
import {
|
|
40
|
+
recordedSchedulerIntervalMinutes,
|
|
41
|
+
selfHostedSchedulerIntervalMinutes,
|
|
42
|
+
} from "../../dist/self-hosted-scheduler-interval.js";
|
|
37
43
|
|
|
38
44
|
const cfg = new pulumi.Config();
|
|
39
45
|
const domain = cfg.require("domain");
|
|
@@ -43,12 +49,26 @@ const ownerEmail = cfg.require("ownerEmail");
|
|
|
43
49
|
const caeloDir = cfg.get("caeloDir") ?? "./.caelo";
|
|
44
50
|
// #553 — same knob as every adapter; undefined = stack-contract default.
|
|
45
51
|
const adminMemory = cfg.get("adminMemory");
|
|
52
|
+
// #1140 — how often the scheduler container calls the admin's tick (1–15
|
|
53
|
+
// minutes, default 5). `init --scheduler-interval-minutes` records it in
|
|
54
|
+
// <caeloDir>/config.json, which the CLI renders from; the stack reads the
|
|
55
|
+
// same record so a `pulumi up` does not put it back to the default, and
|
|
56
|
+
// refuses a Pulumi key that disagrees with it.
|
|
57
|
+
const caeloConfigPath = resolve(caeloDir, "config.json");
|
|
58
|
+
const schedulerIntervalMinutes = selfHostedSchedulerIntervalMinutes(
|
|
59
|
+
cfg.getNumber("schedulerIntervalMinutes"),
|
|
60
|
+
recordedSchedulerIntervalMinutes(caeloConfigPath),
|
|
61
|
+
caeloConfigPath,
|
|
62
|
+
);
|
|
46
63
|
// #696 — the prebuilt, cosign-verified release images, pinned by digest
|
|
47
64
|
// (`ghcr.io/caelo-cms/<svc>@sha256:…`; the generator refuses anything else).
|
|
48
65
|
// `cms-provision init` resolves + verifies them and records them in
|
|
49
66
|
// .caelo/release.json; copy those values here.
|
|
50
67
|
const adminImage = cfg.require("adminImage");
|
|
51
68
|
const gatewayImage = cfg.require("gatewayImage");
|
|
69
|
+
// #1328 — the MCP-Auth-Proxy (remote MCP's sign-in service, its own
|
|
70
|
+
// container and its own host `mcp.<domain>`).
|
|
71
|
+
const mcpAuthProxyImage = cfg.require("mcpAuthProxyImage");
|
|
52
72
|
|
|
53
73
|
const composePath = resolve(caeloDir, "docker-compose.yml");
|
|
54
74
|
const caddyPath = resolve(caeloDir, "Caddyfile");
|
|
@@ -65,22 +85,36 @@ const postgresPassword = pulumi.secret(randomHex(32));
|
|
|
65
85
|
// public_role one (#635: no cms_admin login).
|
|
66
86
|
const adminRolePassword = pulumi.secret(randomHex(32));
|
|
67
87
|
const publicRolePassword = pulumi.secret(randomHex(32));
|
|
88
|
+
// #1329 — the staging gateway's own login on cms_public_staging.
|
|
89
|
+
const publicStagingRolePassword = pulumi.secret(randomHex(32));
|
|
68
90
|
// P18 — the KEK that encrypts stored provider keys.
|
|
69
91
|
const caeloSecretKek = pulumi.secret(randomHex(32));
|
|
70
92
|
|
|
71
93
|
const composeYaml = pulumi
|
|
72
|
-
.all([
|
|
73
|
-
|
|
94
|
+
.all([
|
|
95
|
+
postgresPassword,
|
|
96
|
+
adminRolePassword,
|
|
97
|
+
publicRolePassword,
|
|
98
|
+
publicStagingRolePassword,
|
|
99
|
+
caeloSecretKek,
|
|
100
|
+
])
|
|
101
|
+
.apply(([pgPw, adminPw, publicPw, publicStagingPw, kek]) =>
|
|
74
102
|
generateDockerCompose({
|
|
75
103
|
domain,
|
|
76
104
|
postgresPassword: pgPw,
|
|
77
|
-
rolePasswords: { admin: adminPw, public: publicPw },
|
|
105
|
+
rolePasswords: { admin: adminPw, public: publicPw, publicStaging: publicStagingPw },
|
|
78
106
|
caeloSecretKek: kek,
|
|
79
107
|
anthropicApiKey: process.env.ANTHROPIC_API_KEY,
|
|
80
108
|
resendApiKey: process.env.RESEND_API_KEY,
|
|
81
109
|
diskSize: "20Gi",
|
|
82
110
|
...(adminMemory ? { adminMemory } : {}),
|
|
83
|
-
|
|
111
|
+
...(schedulerIntervalMinutes !== undefined ? { schedulerIntervalMinutes } : {}),
|
|
112
|
+
images: {
|
|
113
|
+
mode: "release",
|
|
114
|
+
admin: adminImage,
|
|
115
|
+
gateway: gatewayImage,
|
|
116
|
+
"mcp-auth-proxy": mcpAuthProxyImage,
|
|
117
|
+
},
|
|
84
118
|
ports: { http: 80, https: 443 },
|
|
85
119
|
project: composeProjectName(domain),
|
|
86
120
|
}),
|
|
@@ -95,6 +129,7 @@ const caddyConf = generateCaddyfile({
|
|
|
95
129
|
stagingSiteRoot: `${SITE_OUTPUT_ROOT}/output/staging/current`,
|
|
96
130
|
adminUpstream: COMPOSE_ADMIN_UPSTREAM,
|
|
97
131
|
gatewayUpstream: COMPOSE_GATEWAY_UPSTREAM,
|
|
132
|
+
mcpAuthProxyUpstream: COMPOSE_MCP_AUTH_PROXY_UPSTREAM,
|
|
98
133
|
domains: selfHostedSeedDomains(domain, stagingHost),
|
|
99
134
|
});
|
|
100
135
|
const bootstrapScript = selfHostedBootstrapScript({ agent: false });
|
|
@@ -134,7 +169,7 @@ const composeUp = new local.Command(
|
|
|
134
169
|
// install's admin image, then everything else.
|
|
135
170
|
create: pulumi.interpolate`set -e
|
|
136
171
|
docker compose -f ${composePath} up -d --wait postgres
|
|
137
|
-
for target in
|
|
172
|
+
for target in ${MIGRATION_TARGETS.join(" ")}; do
|
|
138
173
|
docker compose -f ${composePath} run --rm --no-deps -T caelo-admin bun --bun /app/packages/migrations/src/migrate.ts "$target"
|
|
139
174
|
done
|
|
140
175
|
docker compose -f ${composePath} up -d --remove-orphans`,
|