@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.
Files changed (352) hide show
  1. package/dist/adapter.d.ts +6 -0
  2. package/dist/adapter.d.ts.map +1 -1
  3. package/dist/admin-domain.d.ts +10 -1
  4. package/dist/admin-domain.d.ts.map +1 -1
  5. package/dist/admin-domain.js +13 -3
  6. package/dist/admin-domain.js.map +1 -1
  7. package/dist/adopt-existing.d.ts +12 -3
  8. package/dist/adopt-existing.d.ts.map +1 -1
  9. package/dist/adopt-existing.js +143 -0
  10. package/dist/adopt-existing.js.map +1 -1
  11. package/dist/agent/command.d.ts +23 -0
  12. package/dist/agent/command.d.ts.map +1 -1
  13. package/dist/agent/command.js +32 -9
  14. package/dist/agent/command.js.map +1 -1
  15. package/dist/agent/run.d.ts +2 -0
  16. package/dist/agent/run.d.ts.map +1 -1
  17. package/dist/agent/run.js +5 -1
  18. package/dist/agent/run.js.map +1 -1
  19. package/dist/agent-db.js +69 -10
  20. package/dist/aws-task-roles.d.ts +4 -2
  21. package/dist/aws-task-roles.d.ts.map +1 -1
  22. package/dist/aws-task-roles.js +4 -1
  23. package/dist/aws-task-roles.js.map +1 -1
  24. package/dist/caddy.d.ts +21 -4
  25. package/dist/caddy.d.ts.map +1 -1
  26. package/dist/caddy.js +27 -3
  27. package/dist/caddy.js.map +1 -1
  28. package/dist/cli-routing.js +1 -1
  29. package/dist/cli-routing.js.map +1 -1
  30. package/dist/cli.js +68 -7
  31. package/dist/cli.js.map +1 -1
  32. package/dist/cloudsql-tls.d.ts +93 -0
  33. package/dist/cloudsql-tls.d.ts.map +1 -0
  34. package/dist/cloudsql-tls.js +295 -0
  35. package/dist/cloudsql-tls.js.map +1 -0
  36. package/dist/compose.d.ts +43 -6
  37. package/dist/compose.d.ts.map +1 -1
  38. package/dist/compose.js +148 -23
  39. package/dist/compose.js.map +1 -1
  40. package/dist/converge-checkpoint.d.ts +1 -0
  41. package/dist/converge-checkpoint.d.ts.map +1 -1
  42. package/dist/converge-checkpoint.js +8 -1
  43. package/dist/converge-checkpoint.js.map +1 -1
  44. package/dist/converge-db-trust.d.ts +44 -0
  45. package/dist/converge-db-trust.d.ts.map +1 -0
  46. package/dist/converge-db-trust.js +158 -0
  47. package/dist/converge-db-trust.js.map +1 -0
  48. package/dist/converge-job-gcp.d.ts +5 -2
  49. package/dist/converge-job-gcp.d.ts.map +1 -1
  50. package/dist/converge-job-gcp.js +11 -13
  51. package/dist/converge-job-gcp.js.map +1 -1
  52. package/dist/converge-job-preflight.d.ts.map +1 -1
  53. package/dist/converge-job-preflight.js +8 -0
  54. package/dist/converge-job-preflight.js.map +1 -1
  55. package/dist/converge-job.d.ts +17 -0
  56. package/dist/converge-job.d.ts.map +1 -1
  57. package/dist/converge-job.js +12 -0
  58. package/dist/converge-job.js.map +1 -1
  59. package/dist/deployed-release.d.ts +4 -1
  60. package/dist/deployed-release.d.ts.map +1 -1
  61. package/dist/deployed-release.js +18 -8
  62. package/dist/deployed-release.js.map +1 -1
  63. package/dist/gcp-names.d.ts +34 -0
  64. package/dist/gcp-names.d.ts.map +1 -1
  65. package/dist/gcp-names.js +45 -0
  66. package/dist/gcp-names.js.map +1 -1
  67. package/dist/gcp-url-map.d.ts.map +1 -1
  68. package/dist/gcp-url-map.js +6 -2
  69. package/dist/gcp-url-map.js.map +1 -1
  70. package/dist/index.d.ts +3 -2
  71. package/dist/index.d.ts.map +1 -1
  72. package/dist/index.js +2 -1
  73. package/dist/index.js.map +1 -1
  74. package/dist/infra-contract.d.ts +1 -1
  75. package/dist/infra-contract.d.ts.map +1 -1
  76. package/dist/infra-contract.js +45 -1
  77. package/dist/infra-contract.js.map +1 -1
  78. package/dist/install-job.d.ts +4 -2
  79. package/dist/install-job.d.ts.map +1 -1
  80. package/dist/install-job.js +4 -1
  81. package/dist/install-job.js.map +1 -1
  82. package/dist/install-state.d.ts +13 -0
  83. package/dist/install-state.d.ts.map +1 -1
  84. package/dist/install-state.js +17 -4
  85. package/dist/install-state.js.map +1 -1
  86. package/dist/lifecycle.d.ts.map +1 -1
  87. package/dist/lifecycle.js +20 -8
  88. package/dist/lifecycle.js.map +1 -1
  89. package/dist/mcp-auth-proxy-setup.d.ts +48 -0
  90. package/dist/mcp-auth-proxy-setup.d.ts.map +1 -0
  91. package/dist/mcp-auth-proxy-setup.js +280 -0
  92. package/dist/mcp-auth-proxy-setup.js.map +1 -0
  93. package/dist/migration-runner.d.ts.map +1 -1
  94. package/dist/migration-runner.js +8 -3
  95. package/dist/migration-runner.js.map +1 -1
  96. package/dist/migration-targets.d.ts +11 -0
  97. package/dist/migration-targets.d.ts.map +1 -0
  98. package/dist/migration-targets.js +12 -0
  99. package/dist/migration-targets.js.map +1 -0
  100. package/dist/operator-access.d.ts +10 -0
  101. package/dist/operator-access.d.ts.map +1 -1
  102. package/dist/operator-access.js +5 -0
  103. package/dist/operator-access.js.map +1 -1
  104. package/dist/pool-budget-stack.d.ts +13 -1
  105. package/dist/pool-budget-stack.d.ts.map +1 -1
  106. package/dist/pool-budget-stack.js +11 -1
  107. package/dist/pool-budget-stack.js.map +1 -1
  108. package/dist/public-role-isolation.d.ts +7 -3
  109. package/dist/public-role-isolation.d.ts.map +1 -1
  110. package/dist/public-role-isolation.js +25 -13
  111. package/dist/public-role-isolation.js.map +1 -1
  112. package/dist/release-contract.d.ts.map +1 -1
  113. package/dist/release-contract.js +3 -0
  114. package/dist/release-contract.js.map +1 -1
  115. package/dist/release-manifest.d.ts +25 -1
  116. package/dist/release-manifest.d.ts.map +1 -1
  117. package/dist/release-manifest.js +36 -3
  118. package/dist/release-manifest.js.map +1 -1
  119. package/dist/remote-mcp.d.ts +55 -18
  120. package/dist/remote-mcp.d.ts.map +1 -1
  121. package/dist/remote-mcp.js +307 -77
  122. package/dist/remote-mcp.js.map +1 -1
  123. package/dist/runtime-secrets.d.ts +11 -0
  124. package/dist/runtime-secrets.d.ts.map +1 -1
  125. package/dist/runtime-secrets.js +40 -25
  126. package/dist/runtime-secrets.js.map +1 -1
  127. package/dist/scheduler-setup.d.ts +75 -0
  128. package/dist/scheduler-setup.d.ts.map +1 -0
  129. package/dist/scheduler-setup.js +331 -0
  130. package/dist/scheduler-setup.js.map +1 -0
  131. package/dist/seal-key-rotation.d.ts +7 -7
  132. package/dist/seal-key-rotation.d.ts.map +1 -1
  133. package/dist/seal-key-rotation.js +22 -22
  134. package/dist/seal-key-rotation.js.map +1 -1
  135. package/dist/self-hosted-db-tls.d.ts +37 -0
  136. package/dist/self-hosted-db-tls.d.ts.map +1 -0
  137. package/dist/self-hosted-db-tls.js +220 -0
  138. package/dist/self-hosted-db-tls.js.map +1 -0
  139. package/dist/self-hosted-release.d.ts +3 -1
  140. package/dist/self-hosted-release.d.ts.map +1 -1
  141. package/dist/self-hosted-release.js +33 -9
  142. package/dist/self-hosted-release.js.map +1 -1
  143. package/dist/self-hosted-roles.d.ts +43 -2
  144. package/dist/self-hosted-roles.d.ts.map +1 -1
  145. package/dist/self-hosted-roles.js +87 -1
  146. package/dist/self-hosted-roles.js.map +1 -1
  147. package/dist/self-hosted-scheduler-interval.d.ts +21 -0
  148. package/dist/self-hosted-scheduler-interval.d.ts.map +1 -0
  149. package/dist/self-hosted-scheduler-interval.js +68 -0
  150. package/dist/self-hosted-scheduler-interval.js.map +1 -0
  151. package/dist/self-hosted-start.d.ts +0 -21
  152. package/dist/self-hosted-start.d.ts.map +1 -1
  153. package/dist/self-hosted-start.js +39 -1
  154. package/dist/self-hosted-start.js.map +1 -1
  155. package/dist/self-hosted-upgrade.d.ts +19 -7
  156. package/dist/self-hosted-upgrade.d.ts.map +1 -1
  157. package/dist/self-hosted-upgrade.js +78 -25
  158. package/dist/self-hosted-upgrade.js.map +1 -1
  159. package/dist/stack-contract.d.ts +234 -28
  160. package/dist/stack-contract.d.ts.map +1 -1
  161. package/dist/stack-contract.js +397 -39
  162. package/dist/stack-contract.js.map +1 -1
  163. package/dist/stack-converge.d.ts +31 -0
  164. package/dist/stack-converge.d.ts.map +1 -1
  165. package/dist/stack-converge.js +71 -30
  166. package/dist/stack-converge.js.map +1 -1
  167. package/dist/staging-database.d.ts +22 -0
  168. package/dist/staging-database.d.ts.map +1 -0
  169. package/dist/staging-database.js +117 -0
  170. package/dist/staging-database.js.map +1 -0
  171. package/dist/upgrade/app-roll.d.ts +25 -4
  172. package/dist/upgrade/app-roll.d.ts.map +1 -1
  173. package/dist/upgrade/app-roll.js +63 -17
  174. package/dist/upgrade/app-roll.js.map +1 -1
  175. package/dist/upgrade/converge.d.ts +5 -0
  176. package/dist/upgrade/converge.d.ts.map +1 -1
  177. package/dist/upgrade/converge.js +207 -20
  178. package/dist/upgrade/converge.js.map +1 -1
  179. package/dist/upgrade/health.d.ts +31 -7
  180. package/dist/upgrade/health.d.ts.map +1 -1
  181. package/dist/upgrade/health.js +41 -17
  182. package/dist/upgrade/health.js.map +1 -1
  183. package/dist/upgrade/image-forms.d.ts +1 -1
  184. package/dist/upgrade/image-forms.d.ts.map +1 -1
  185. package/dist/upgrade/image-forms.js +24 -7
  186. package/dist/upgrade/image-forms.js.map +1 -1
  187. package/dist/upgrade/release-images.d.ts +12 -3
  188. package/dist/upgrade/release-images.d.ts.map +1 -1
  189. package/dist/upgrade/release-images.js +13 -4
  190. package/dist/upgrade/release-images.js.map +1 -1
  191. package/dist/upgrade/upgrade.d.ts +19 -3
  192. package/dist/upgrade/upgrade.d.ts.map +1 -1
  193. package/dist/upgrade/upgrade.js +37 -2
  194. package/dist/upgrade/upgrade.js.map +1 -1
  195. package/dist/upgrader-gcp.d.ts +13 -0
  196. package/dist/upgrader-gcp.d.ts.map +1 -1
  197. package/dist/upgrader-gcp.js +50 -1
  198. package/dist/upgrader-gcp.js.map +1 -1
  199. package/dist/upgrader-iam-preflight.d.ts.map +1 -1
  200. package/dist/upgrader-iam-preflight.js +6 -1
  201. package/dist/upgrader-iam-preflight.js.map +1 -1
  202. package/dist/upgrader-job.d.ts +1 -1
  203. package/dist/upgrader-job.d.ts.map +1 -1
  204. package/dist/upgrader-job.js +20 -14
  205. package/dist/upgrader-job.js.map +1 -1
  206. package/dist/upgrader-records.d.ts +1 -1
  207. package/dist/upgrader-records.d.ts.map +1 -1
  208. package/dist/upgrader-records.js +26 -7
  209. package/dist/upgrader-records.js.map +1 -1
  210. package/dist/upgrader-setup.d.ts.map +1 -1
  211. package/dist/upgrader-setup.js +10 -5
  212. package/dist/upgrader-setup.js.map +1 -1
  213. package/dist/wizard.d.ts +7 -0
  214. package/dist/wizard.d.ts.map +1 -1
  215. package/dist/wizard.js +11 -0
  216. package/dist/wizard.js.map +1 -1
  217. package/dist/wizards/gcp-cost.d.ts +31 -0
  218. package/dist/wizards/gcp-cost.d.ts.map +1 -1
  219. package/dist/wizards/gcp-cost.js +103 -18
  220. package/dist/wizards/gcp-cost.js.map +1 -1
  221. package/dist/wizards/gcp.d.ts.map +1 -1
  222. package/dist/wizards/gcp.js +129 -27
  223. package/dist/wizards/gcp.js.map +1 -1
  224. package/package.json +2 -2
  225. package/src/adapter.ts +6 -0
  226. package/src/admin-domain.test.ts +9 -8
  227. package/src/admin-domain.ts +13 -3
  228. package/src/admin-memory.test.ts +2 -1
  229. package/src/adopt-existing.test.ts +85 -0
  230. package/src/adopt-existing.ts +141 -3
  231. package/src/agent/command.test.ts +49 -1
  232. package/src/agent/command.ts +39 -9
  233. package/src/agent/run.test.ts +19 -0
  234. package/src/agent/run.ts +11 -2
  235. package/src/aws-task-roles.ts +5 -2
  236. package/src/caddy-redirects.docker.test.ts +2 -0
  237. package/src/caddy-redirects.test.ts +4 -1
  238. package/src/caddy-validate.test.ts +7 -1
  239. package/src/caddy.test.ts +39 -1
  240. package/src/caddy.ts +36 -4
  241. package/src/checkpoint-secrets.test.ts +14 -6
  242. package/src/cli-routing.test.ts +1 -1
  243. package/src/cli-routing.ts +1 -1
  244. package/src/cli.ts +71 -5
  245. package/src/cloudsql-tls.test.ts +255 -0
  246. package/src/cloudsql-tls.ts +359 -0
  247. package/src/compose.ts +193 -24
  248. package/src/converge-checkpoint.ts +8 -1
  249. package/src/converge-db-trust.test.ts +237 -0
  250. package/src/converge-db-trust.ts +212 -0
  251. package/src/converge-job-gcp.ts +11 -12
  252. package/src/converge-job-preflight.test.ts +5 -0
  253. package/src/converge-job-preflight.ts +8 -0
  254. package/src/converge-job.test.ts +70 -0
  255. package/src/converge-job.ts +32 -0
  256. package/src/database-tls-builders.test.ts +56 -0
  257. package/src/deployed-release.test.ts +107 -40
  258. package/src/deployed-release.ts +30 -9
  259. package/src/gateway-env-isolation.test.ts +50 -12
  260. package/src/gcp-names.ts +56 -0
  261. package/src/gcp-url-map.test.ts +6 -8
  262. package/src/gcp-url-map.ts +6 -2
  263. package/src/index.test.ts +10 -4
  264. package/src/index.ts +7 -0
  265. package/src/infra-contract.test.ts +7 -3
  266. package/src/infra-contract.ts +45 -1
  267. package/src/install-job.ts +4 -1
  268. package/src/install-state.ts +33 -4
  269. package/src/lifecycle.ts +23 -11
  270. package/src/mcp-auth-proxy-setup.test.ts +305 -0
  271. package/src/mcp-auth-proxy-setup.ts +374 -0
  272. package/src/mcp-iap-stacks.test.ts +1 -1
  273. package/src/media-volume.test.ts +1 -1
  274. package/src/migration-runner.ts +14 -9
  275. package/src/migration-targets.ts +12 -0
  276. package/src/operator-access-stacks.test.ts +1 -1
  277. package/src/operator-access.test.ts +20 -4
  278. package/src/operator-access.ts +14 -0
  279. package/src/pool-budget-stack.ts +15 -0
  280. package/src/pool-budget-stacks.test.ts +39 -14
  281. package/src/public-role-isolation.test.ts +18 -8
  282. package/src/public-role-isolation.ts +31 -17
  283. package/src/release-contract.ts +3 -0
  284. package/src/release-manifest.test.ts +25 -0
  285. package/src/release-manifest.ts +59 -4
  286. package/src/remote-mcp-stack-config.test.ts +19 -9
  287. package/src/remote-mcp.test.ts +252 -57
  288. package/src/remote-mcp.ts +336 -77
  289. package/src/runtime-secrets.test.ts +37 -14
  290. package/src/runtime-secrets.ts +87 -27
  291. package/src/scheduler-setup.test.ts +201 -0
  292. package/src/scheduler-setup.ts +375 -0
  293. package/src/seal-key-rotation.test.ts +2 -2
  294. package/src/seal-key-rotation.ts +25 -25
  295. package/src/self-hosted-backup.test.ts +1 -0
  296. package/src/self-hosted-compose.test.ts +180 -6
  297. package/src/self-hosted-db-tls.test.ts +111 -0
  298. package/src/self-hosted-db-tls.ts +287 -0
  299. package/src/self-hosted-release-resolve.test.ts +71 -0
  300. package/src/self-hosted-release.ts +41 -11
  301. package/src/self-hosted-roles.integration.test.ts +2 -0
  302. package/src/self-hosted-roles.test.ts +87 -3
  303. package/src/self-hosted-roles.ts +101 -3
  304. package/src/self-hosted-scheduler-interval.test.ts +66 -0
  305. package/src/self-hosted-scheduler-interval.ts +79 -0
  306. package/src/self-hosted-start.test.ts +35 -2
  307. package/src/self-hosted-start.ts +20 -2
  308. package/src/self-hosted-upgrade.test.ts +104 -14
  309. package/src/self-hosted-upgrade.ts +123 -31
  310. package/src/stack-contract-parity.test.ts +79 -12
  311. package/src/stack-contract.test.ts +199 -31
  312. package/src/stack-contract.ts +472 -45
  313. package/src/stack-converge-secrets.test.ts +28 -13
  314. package/src/stack-converge.test.ts +203 -28
  315. package/src/stack-converge.ts +92 -30
  316. package/src/staging-database.test.ts +114 -0
  317. package/src/staging-database.ts +148 -0
  318. package/src/state-store.test.ts +2 -1
  319. package/src/upgrade/app-roll.ts +97 -19
  320. package/src/upgrade/converge.ts +249 -19
  321. package/src/upgrade/health.test.ts +63 -25
  322. package/src/upgrade/health.ts +54 -22
  323. package/src/upgrade/image-forms.test.ts +34 -0
  324. package/src/upgrade/image-forms.ts +23 -8
  325. package/src/upgrade/release-images.test.ts +5 -1
  326. package/src/upgrade/release-images.ts +22 -6
  327. package/src/upgrade/upgrade.test.ts +438 -46
  328. package/src/upgrade/upgrade.ts +64 -6
  329. package/src/upgrader-gcp.ts +66 -3
  330. package/src/upgrader-iam-preflight.test.ts +19 -0
  331. package/src/upgrader-iam-preflight.ts +6 -0
  332. package/src/upgrader-job.test.ts +101 -27
  333. package/src/upgrader-job.ts +27 -14
  334. package/src/upgrader-records.test.ts +97 -12
  335. package/src/upgrader-records.ts +28 -11
  336. package/src/upgrader-setup.test.ts +2 -0
  337. package/src/upgrader-setup.ts +10 -4
  338. package/src/wizard.ts +19 -0
  339. package/src/wizards/gcp-cost.test.ts +103 -0
  340. package/src/wizards/gcp-cost.ts +125 -18
  341. package/src/wizards/gcp.ts +164 -22
  342. package/stacks/aws/Pulumi.yaml +1 -1
  343. package/stacks/aws/index.ts +72 -14
  344. package/stacks/azure/index.ts +108 -21
  345. package/stacks/gcp/Pulumi.yaml +1 -1
  346. package/stacks/gcp/README.md +1 -0
  347. package/stacks/gcp/index.ts +133 -24
  348. package/stacks/gcp-firebase/Pulumi.yaml +1 -1
  349. package/stacks/gcp-firebase/index.ts +212 -34
  350. package/stacks/self-hosted/Pulumi.yaml +3 -0
  351. package/stacks/self-hosted/README.md +6 -1
  352. package/stacks/self-hosted/index.ts +40 -5
package/src/index.test.ts CHANGED
@@ -22,6 +22,7 @@ describe("Caddyfile generator", () => {
22
22
  tls: "acme",
23
23
  adminUpstream: "caelo-admin:5173",
24
24
  gatewayUpstream: "caelo-gateway:8090",
25
+ mcpAuthProxyUpstream: "caelo-mcp-auth-proxy:8095",
25
26
  domains: [
26
27
  { hostname: "example.com", kind: "public", env: "production" },
27
28
  { hostname: "staging.example.com", kind: "public", env: "staging" },
@@ -49,6 +50,7 @@ describe("Caddyfile generator", () => {
49
50
  tls: "acme",
50
51
  adminUpstream: "caelo-admin:5173",
51
52
  gatewayUpstream: "caelo-gateway:8090",
53
+ mcpAuthProxyUpstream: "caelo-mcp-auth-proxy:8095",
52
54
  domains: [],
53
55
  });
54
56
  expect(out).toContain(":8081 {");
@@ -64,6 +66,7 @@ describe("Caddy cache policy for content-hashed assets", () => {
64
66
  tls: "acme",
65
67
  adminUpstream: "caelo-admin:5173",
66
68
  gatewayUpstream: "caelo-gateway:8090",
69
+ mcpAuthProxyUpstream: "caelo-mcp-auth-proxy:8095",
67
70
  domains: [
68
71
  { hostname: "example.com", kind: "public", env: "production" },
69
72
  { hostname: "admin.example.com", kind: "admin", env: "production" },
@@ -149,8 +152,9 @@ describe("docker-compose generator", () => {
149
152
  const out = generateDockerCompose({
150
153
  domain: "example.com",
151
154
  postgresPassword: "supersecret",
152
- rolePasswords: { admin: "adminpw", public: "publicpw" },
155
+ rolePasswords: { admin: "adminpw", public: "publicpw", publicStaging: "ps" },
153
156
  caeloSecretKek: "0".repeat(64),
157
+ dbTlsServerCertSha256: "abababababababababababababababababababababababababababababababab",
154
158
  internalSecret: "i".repeat(48),
155
159
  anthropicApiKey: "sk-ant-…",
156
160
  diskSize: "20Gi",
@@ -171,8 +175,9 @@ describe("docker-compose generator", () => {
171
175
  const out = generateDockerCompose({
172
176
  domain: "example.com",
173
177
  postgresPassword: "supersecret",
174
- rolePasswords: { admin: "adminpw", public: "publicpw" },
178
+ rolePasswords: { admin: "adminpw", public: "publicpw", publicStaging: "ps" },
175
179
  caeloSecretKek: "0".repeat(64),
180
+ dbTlsServerCertSha256: "abababababababababababababababababababababababababababababababab",
176
181
  internalSecret: "i".repeat(48),
177
182
  diskSize: "20Gi",
178
183
  images: RELEASE_IMAGES,
@@ -181,7 +186,7 @@ describe("docker-compose generator", () => {
181
186
  });
182
187
  const gateway = out.slice(out.indexOf("caelo-gateway:"), out.indexOf("\nvolumes:"));
183
188
  expect(gateway).toContain(
184
- 'PUBLIC_DATABASE_URL: "postgres://public_role:publicpw@postgres:5432/cms_public"',
189
+ 'PUBLIC_DATABASE_URL: "postgres://public_role:publicpw@postgres:5432/cms_public?sslmode=verify-full&sslrootcert=/etc/caelo/db-ca/self-hosted-ca.pem"',
185
190
  );
186
191
  expect(gateway).not.toMatch(
187
192
  /ADMIN_DATABASE|GATEWAY_DATABASE|cms_admin|admin_role|gateway_role|adminpw|gatewaypw|supersecret|CAELO_SECRET_KEK/,
@@ -196,8 +201,9 @@ describe("docker-compose generator", () => {
196
201
  const out = generateDockerCompose({
197
202
  domain: "example.com",
198
203
  postgresPassword: 'pa"ss',
199
- rolePasswords: { admin: "a", public: "b" },
204
+ rolePasswords: { admin: "a", public: "b", publicStaging: "ps" },
200
205
  caeloSecretKek: "0".repeat(64),
206
+ dbTlsServerCertSha256: "abababababababababababababababababababababababababababababababab",
201
207
  internalSecret: "i".repeat(48),
202
208
  diskSize: "1Gi",
203
209
  images: RELEASE_IMAGES,
package/src/index.ts CHANGED
@@ -46,12 +46,18 @@ export {
46
46
  selfHostedCdnCopy,
47
47
  } from "./cdn-copy.js";
48
48
  export { type ComposeSpec, generateDockerCompose, type RolePasswords } from "./compose.js";
49
+ export {
50
+ type ConvergeDatabaseTarget,
51
+ convergeDatabaseTarget,
52
+ serverIdentityProblem,
53
+ } from "./converge-db-trust.js";
49
54
  export {
50
55
  type ConvergeClaim,
51
56
  type ConvergeJobDeps,
52
57
  type ConvergeQueue,
53
58
  type ConvergeUpgradeOutcome,
54
59
  runConvergeJob,
60
+ type SecureDatabaseResult,
55
61
  } from "./converge-job.js";
56
62
  export { convergeExecutionNameError, gcpConvergeDeps } from "./converge-job-gcp.js";
57
63
  export { INFRA_CONTRACT_VERSION } from "./infra-contract.js";
@@ -96,6 +102,7 @@ export {
96
102
  type RequestDecision,
97
103
  } from "./upgrader-assess.js";
98
104
  export {
105
+ convergeDatabaseHost,
99
106
  gcpUpgraderDeps,
100
107
  type UpgraderPlacement,
101
108
  upgraderDatabaseHost,
@@ -20,8 +20,8 @@ describe("infrastructure contract steps", () => {
20
20
  );
21
21
  });
22
22
 
23
- it("is at 14: public_role revoke (3), URL-map redirects role (4), no gateway cms_admin login (5), ops-bucket list (6), gateway proxy hops (7), quality-audit job (8), converge job (9), remote MCP (10), Google sign-in (11), project-level sign-in client (12), edge security headers (13), capture job (14)", () => {
24
- expect(INFRA_CONTRACT_VERSION).toBe(14);
23
+ it("is at 18: public_role revoke (3), URL-map redirects role (4), no gateway cms_admin login (5), ops-bucket list (6), gateway proxy hops (7), quality-audit job (8), converge job (9), remote MCP (10), Google sign-in (11), project-level sign-in client (12), edge security headers (13), capture job (14), database verify-full TLS (15), MCP-Auth-Proxy (16), staging database (17), scheduler tick (18)", () => {
24
+ expect(INFRA_CONTRACT_VERSION).toBe(18);
25
25
  expect(INFRA_CONTRACT_STEPS[3]).toContain("#633");
26
26
  expect(INFRA_CONTRACT_STEPS[4]).toContain("#587");
27
27
  expect(INFRA_CONTRACT_STEPS[5]).toContain("#635");
@@ -34,6 +34,10 @@ describe("infrastructure contract steps", () => {
34
34
  expect(INFRA_CONTRACT_STEPS[12]).toContain("project's IAP settings");
35
35
  expect(INFRA_CONTRACT_STEPS[13]).toContain("#891");
36
36
  expect(INFRA_CONTRACT_STEPS[14]).toContain("#1217");
37
+ expect(INFRA_CONTRACT_STEPS[15]).toContain("#1338");
38
+ expect(INFRA_CONTRACT_STEPS[16]).toContain("#1328");
39
+ expect(INFRA_CONTRACT_STEPS[17]).toContain("#1329");
40
+ expect(INFRA_CONTRACT_STEPS[18]).toContain("#1140");
37
41
  });
38
42
 
39
43
  it("lists the pending steps oldest first", () => {
@@ -44,6 +48,6 @@ describe("infrastructure contract steps", () => {
44
48
  });
45
49
 
46
50
  it("names a version a newer release added by number", () => {
47
- expect(infraStepsText(14, 15)).toBe("infrastructure step 15 (see that release's notes)");
51
+ expect(infraStepsText(18, 19)).toBe("infrastructure step 19 (see that release's notes)");
48
52
  });
49
53
  });
@@ -22,7 +22,7 @@
22
22
  * whose app-roll references infrastructure an install does not have yet fails
23
23
  * at roll time, after migrations ran.
24
24
  */
25
- export const INFRA_CONTRACT_VERSION = 14;
25
+ export const INFRA_CONTRACT_VERSION = 18;
26
26
 
27
27
  // History (why each bump happened — an install below a number needs the
28
28
  // converge that introduced it):
@@ -110,6 +110,46 @@ export const INFRA_CONTRACT_VERSION = 14;
110
110
  // CAELO_CAPTURE_BUCKET / CAELO_MEDIA_BUCKET on the admin. The new admin
111
111
  // runs no browser itself on GCP and the app-roll moves the job's image,
112
112
  // so it must exist before an in-cloud upgrader rolls onto it.
113
+ // 15 — #1338: verify-full TLS to Cloud SQL. The instance moves to the
114
+ // shared server CA (a patch plus a server-certificate rotation) and
115
+ // refuses plaintext, a private Cloud DNS zone resolves its DNS name in
116
+ // the VPC, and the services dial that name (cloudsql-tls.ts). The new
117
+ // images refuse any database URL weaker than verify-full, and the
118
+ // in-cloud upgrader may neither patch the instance nor create a DNS
119
+ // zone, so it must not roll an install onto them first.
120
+ // 16 — #1328: the MCP-Auth-Proxy, remote MCP's broker as a service of its
121
+ // own (no database, its own image). Its service account with
122
+ // secretAccessor on exactly the Google sign-in client and the seal
123
+ // key, and the upgrader's actAs on it. On `gcp-firebase` the visitor
124
+ // gateway moves to a new Cloud Run service (`caelo-<env>-data-gateway`,
125
+ // public invoker, the admin's run.viewer for the Firebase rewrite) and
126
+ // the old `gateway` service, whose run.app URL every connector holds
127
+ // (E13 a), becomes the proxy: new image, new account, no VPC, no
128
+ // database env. On `gcp` the proxy is a new service behind its own
129
+ // serverless NEG, backend service and Cloud Armor policy (the MCP rule
130
+ // moves there) and the URL map sends the MCP paths to it. New
131
+ // services, accounts, IAM and load-balancer resources — none of which
132
+ // the in-cloud upgrader may create, and the app-roll rolls three
133
+ // services from now on.
134
+ // 17 — #1329: staging's own database. The Cloud SQL database
135
+ // `cms_public_staging`, the Admin API user `public_staging_role` (its
136
+ // password the new CLI-generated secret `public-staging-role-password`)
137
+ // and its membership revoke; the admin's PUBLIC_STAGING_ADMIN_DATABASE_URL
138
+ // and the gateway's CAELO_GATEWAY_ENVIRONMENT. The in-cloud upgrader
139
+ // may create no database, user or secret, and the new admin and
140
+ // gateway refuse to start without them. Additive only (converge
141
+ // release 1 of the gateway hardening): nothing is served from the
142
+ // staging database yet, so rolling back stays harmless.
143
+ // 18 — #1140: the scheduler tick (scheduler-setup.ts). Its service account
144
+ // (token creator on itself, workflows.invoker, logWriter, IAP access to
145
+ // the admin), the Cloud Workflows workflow that signs its IAP JWT and
146
+ // calls the admin's /api/internal/scheduler/tick, the one Cloud
147
+ // Scheduler job that starts it every schedulerIntervalMinutes, the
148
+ // operator-access job's CAELO_OPERATOR_ACCESS_IAP_SERVICE_ACCOUNTS (so
149
+ // its sync keeps the account on IAP) and CAELO_SCHEDULER_SERVICE_ACCOUNT
150
+ // on the admin. The new admin runs its periodic work and plugin workers
151
+ // only on that tick, so an in-cloud upgrader must not roll an install
152
+ // onto it before the tick exists.
113
153
 
114
154
  /**
115
155
  * What each contract version adds, in operator terms — the upgrader's
@@ -131,6 +171,10 @@ export const INFRA_CONTRACT_STEPS: Readonly<Record<number, string>> = {
131
171
  12: "Google sign-in at project level: the operator-access job's permission to set the sign-in client in the project's IAP settings (#914)",
132
172
  13: "the public site's security headers at the edge: nosniff on every file, uploaded SVGs no longer run as documents on the site's origin, and a one-time CDN purge (#891)",
133
173
  14: "the capture job that takes server-side screenshots outside the admin, its service account, bucket and grants (#1217)",
174
+ 15: "verified TLS to the database: Cloud SQL's shared server CA, encrypted connections only, and a private DNS name the services dial (#1338)",
175
+ 16: "the MCP sign-in service as its own Cloud Run service with its own account (no database access), the visitor gateway as its own service on Firebase installs, and on Google Cloud load-balancer installs the sign-in service's backend and Cloud Armor rule (#1328)",
176
+ 17: "the staging site's own database and its gateway login, kept apart from the live data (#1329)",
177
+ 18: "the scheduler tick that runs the admin's periodic work and plugin workers every few minutes: its service account, workflow and Cloud Scheduler job, and its IAP access (#1140)",
134
178
  };
135
179
 
136
180
  /**
@@ -65,13 +65,16 @@ export const OPS_IMAGE_REPOSITORY =
65
65
  * Roles the provisioner needs on top of {@link PROVISIONER_ROLE_LIST} when it
66
66
  * runs the install job instead of the operator: the operator-access sync job
67
67
  * creates a project custom role and a Cloud Scheduler job (operator-access.ts),
68
- * and the job's own output has to reach Cloud Logging so the terminal can
68
+ * the scheduler tick deploys a Cloud Workflows workflow (scheduler-setup.ts,
69
+ * #1140; the converge job gets the same roles, converge-job-setup.ts), and
70
+ * the job's own output has to reach Cloud Logging so the terminal can
69
71
  * tail it. Granted only to installs that use the job. Not an escalation: the
70
72
  * provisioner already holds `resourcemanager.projectIamAdmin`.
71
73
  */
72
74
  export const INSTALL_JOB_ROLES = [
73
75
  "roles/iam.roleAdmin",
74
76
  "roles/cloudscheduler.admin",
77
+ "roles/workflows.editor",
75
78
  "roles/logging.logWriter",
76
79
  ] as const;
77
80
 
@@ -115,6 +115,13 @@ export interface InstallMetadata {
115
115
  * run while it is lower than the release's (upgrade/converge.ts).
116
116
  */
117
117
  infraContractVersion?: number;
118
+ /**
119
+ * #1140 — the `schedulerIntervalMinutes` knob: how often the one Cloud
120
+ * Scheduler job calls the admin's tick (1–15 minutes; `--scheduler-
121
+ * interval-minutes` on the wizard). Absent: the default (5,
122
+ * stack-contract.ts SCHEDULER_INTERVAL_MINUTES).
123
+ */
124
+ schedulerIntervalMinutes?: number;
118
125
  }
119
126
 
120
127
  /** The install's staging host: its recorded setting, else the default. */
@@ -155,10 +162,25 @@ export function recordableStagingHost(host: string): string | null {
155
162
  export interface ImageDigests {
156
163
  readonly admin: string;
157
164
  readonly gateway: string;
165
+ /**
166
+ * #1328 — the MCP-Auth-Proxy's. Absent on installs recorded before it
167
+ * existed: their first upgrade to a release with it records it, and a
168
+ * stack run without it deploys the release tag's proxy image.
169
+ */
170
+ readonly "mcp-auth-proxy"?: string;
158
171
  }
159
172
 
160
173
  const DIGEST = /^sha256:[0-9a-f]{64}$/;
161
174
 
175
+ /** {@link ImageDigests} without the keys that are absent (a JSON-stable copy). */
176
+ function imageDigestsRecord(d: ImageDigests): ImageDigests {
177
+ return {
178
+ admin: d.admin,
179
+ gateway: d.gateway,
180
+ ...(d["mcp-auth-proxy"] ? { "mcp-auth-proxy": d["mcp-auth-proxy"] } : {}),
181
+ };
182
+ }
183
+
162
184
  /**
163
185
  * The image digests recorded for an install, or null when none are. Throws
164
186
  * on a malformed record rather than deploying a guess.
@@ -166,7 +188,12 @@ const DIGEST = /^sha256:[0-9a-f]{64}$/;
166
188
  export function recordedImageDigests(meta: InstallMetadata): ImageDigests | null {
167
189
  const d = meta.imageDigests;
168
190
  if (d === undefined) return null;
169
- if (!DIGEST.test(d.admin ?? "") || !DIGEST.test(d.gateway ?? "")) {
191
+ const proxy = d["mcp-auth-proxy"];
192
+ if (
193
+ !DIGEST.test(d.admin ?? "") ||
194
+ !DIGEST.test(d.gateway ?? "") ||
195
+ (proxy !== undefined && !DIGEST.test(proxy))
196
+ ) {
170
197
  throw new Error(
171
198
  `install.json imageDigests is malformed (${JSON.stringify(d)}); fix or remove it, then re-run.`,
172
199
  );
@@ -211,15 +238,17 @@ export function recordImageDigests(
211
238
  // drop it — a stale one would be worse than none (the upgrader job
212
239
  // refuses on none and judges downgrades by it).
213
240
  const same =
214
- meta.imageDigests?.admin === digests.admin && meta.imageDigests?.gateway === digests.gateway;
241
+ meta.imageDigests?.admin === digests.admin &&
242
+ meta.imageDigests?.gateway === digests.gateway &&
243
+ meta.imageDigests?.["mcp-auth-proxy"] === digests["mcp-auth-proxy"];
215
244
  const { releaseVersion: previous, platformDigests: previousPlatform, ...rest } = meta;
216
245
  const version = releaseVersion ?? (same ? previous : undefined);
217
246
  // The platform forms belong to their indexes just as the version does.
218
247
  const platform = platformDigests ?? (same ? previousPlatform : undefined);
219
248
  writeMetadata(installId, {
220
249
  ...rest,
221
- imageDigests: { admin: digests.admin, gateway: digests.gateway },
222
- ...(platform ? { platformDigests: { admin: platform.admin, gateway: platform.gateway } } : {}),
250
+ imageDigests: imageDigestsRecord(digests),
251
+ ...(platform ? { platformDigests: imageDigestsRecord(platform) } : {}),
223
252
  ...(version ? { releaseVersion: version } : {}),
224
253
  });
225
254
  }
package/src/lifecycle.ts CHANGED
@@ -60,6 +60,7 @@ import {
60
60
  } from "./runtime-secrets.js";
61
61
  import { finishSealKeyRotation, startSealKeyRotation } from "./seal-key-rotation.js";
62
62
  import { selfCommand } from "./self-command.js";
63
+ import { CLOUD_RUN_SLUGS, type CloudRunSlug, cloudRunServicePrefix } from "./stack-contract.js";
63
64
  import {
64
65
  backendShellEnv,
65
66
  localStateNotice,
@@ -640,6 +641,9 @@ async function upgradeLockedInstall(
640
641
  domain: meta.domain,
641
642
  stagingHost,
642
643
  ownerEmail: meta.ownerEmail,
644
+ ...(meta.schedulerIntervalMinutes !== undefined
645
+ ? { schedulerIntervalMinutes: meta.schedulerIntervalMinutes }
646
+ : {}),
643
647
  targetTag,
644
648
  infraContractVersion: INFRA_CONTRACT_VERSION,
645
649
  skipVerify: opts.skipVerify === true,
@@ -720,6 +724,7 @@ async function gcpHealthProbe(
720
724
  domain: install.domain,
721
725
  adminServiceUrl: urls.admin as string,
722
726
  gatewayServiceUrl: urls.gateway as string,
727
+ mcpAuthProxyServiceUrl: urls["mcp-auth-proxy"] as string,
723
728
  },
724
729
  { run, http, sleep: realSleep, accessJustChanged: context.accessJustChanged },
725
730
  ),
@@ -880,23 +885,29 @@ export async function rotateSecretCommand(
880
885
  }
881
886
 
882
887
  if (name === SEAL_KEY_SECRET) {
883
- const gateway = await resolveGcpResourceName(
888
+ // #1328 — the broker, and with it the seal key, lives in the MCP-Auth-Proxy.
889
+ const proxyPrefix = cloudRunServicePrefix(meta.provider, GCP_STACK_ENV, "mcp-auth-proxy");
890
+ const proxy = await resolveGcpResourceName(
884
891
  gcloud,
885
892
  "run-service",
886
- "caelo-production-gateway",
893
+ proxyPrefix,
887
894
  meta.projectId,
888
895
  region,
889
896
  );
890
- if (gateway.status !== "found") {
897
+ if (proxy.status !== "found") {
891
898
  log.error(
892
899
  red(
893
- `${lookupFailure(gateway, "caelo-production-gateway* Cloud Run service", "operator")} — nothing rotated`,
900
+ `${lookupFailure(proxy, `${proxyPrefix}* Cloud Run service`, "operator")} — nothing rotated`,
894
901
  ),
895
902
  );
896
903
  return;
897
904
  }
898
- const gatewayService = gateway.name;
899
- const target = { projectId: meta.projectId, region, env: GCP_STACK_ENV, gatewayService };
905
+ const target = {
906
+ projectId: meta.projectId,
907
+ region,
908
+ env: GCP_STACK_ENV,
909
+ proxyService: proxy.name,
910
+ };
900
911
  const report = opts.finish
901
912
  ? await finishSealKeyRotation(target, {}, { now: opts.now === true })
902
913
  : await startSealKeyRotation(target);
@@ -910,19 +921,20 @@ export async function rotateSecretCommand(
910
921
 
911
922
  const s = spinner();
912
923
  s.start(`Rotating ${secret}...`);
913
- const services: Partial<Record<"admin" | "gateway", string>> = {};
914
- for (const slug of ["admin", "gateway"] as const) {
924
+ const services: Partial<Record<CloudRunSlug, string>> = {};
925
+ for (const slug of CLOUD_RUN_SLUGS) {
926
+ const prefix = cloudRunServicePrefix(meta.provider, GCP_STACK_ENV, slug);
915
927
  const service = await resolveGcpResourceName(
916
928
  gcloud,
917
929
  "run-service",
918
- `caelo-production-${slug}`,
930
+ prefix,
919
931
  meta.projectId,
920
932
  region,
921
933
  );
922
934
  if (service.status !== "found") {
923
935
  s.stop(
924
936
  red(
925
- `${lookupFailure(service, `caelo-production-${slug}* Cloud Run service`, "operator")} — nothing rotated`,
937
+ `${lookupFailure(service, `${prefix}* Cloud Run service`, "operator")} — nothing rotated`,
926
938
  ),
927
939
  );
928
940
  return;
@@ -952,7 +964,7 @@ export async function rotateSecretCommand(
952
964
  projectId: meta.projectId,
953
965
  region,
954
966
  env: GCP_STACK_ENV,
955
- services: services as Record<"admin" | "gateway", string>,
967
+ services: services as Record<CloudRunSlug, string>,
956
968
  ...(sqlInstance ? { sqlInstance } : {}),
957
969
  },
958
970
  secret,