@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
@@ -4,14 +4,17 @@
4
4
  * `cms-provision upgrade` for one gcp / gcp-firebase install: converge, then
5
5
  * app-roll (epic #696). The order, and why:
6
6
  *
7
- * 1. plan (read-only): release digests, both services, the env contract
7
+ * 1. plan: release digests, the services, the env contract — read-only
8
+ * but for the services an older install lacks (#1328, additive and
9
+ * serving nothing yet: mcp-auth-proxy-setup.ts)
8
10
  * 2. verify the images' signatures
9
11
  * 3. pre-upgrade Cloud SQL backup — the first change; if it fails, the
10
12
  * install is exactly as it was
11
13
  * 4. converge, the part before the roll (keyless provisioner, IAM, secrets,
12
14
  * MCP access)
13
15
  * 5. app-roll: health baseline, migrations, the converge steps that must
14
- * follow migrations, admin + gateway rolls, deep health check, rollback
16
+ * follow migrations, admin, gateway and MCP-Auth-Proxy rolls, deep
17
+ * health check, rollback
15
18
  * on failure, recorded digests
16
19
  * 6. converge, the part after the roll; a complete converge records the
17
20
  * install's infrastructure contract version
@@ -29,6 +32,7 @@
29
32
 
30
33
  import type { GcloudRunner, Sleep } from "../gcloud-retry.js";
31
34
  import type { ImageDigests } from "../install-state.js";
35
+ import { ensureReleaseServices } from "../mcp-auth-proxy-setup.js";
32
36
  import type { HttpFetch } from "../runtime-secrets.js";
33
37
  import {
34
38
  type AppRollPlan,
@@ -53,6 +57,12 @@ export interface GcpUpgradeTarget {
53
57
  readonly domain: string;
54
58
  readonly stagingHost: string;
55
59
  readonly ownerEmail: string;
60
+ /**
61
+ * #1140 — install.json's `schedulerIntervalMinutes` (absent: the default).
62
+ * Bounded to 1–15 by the converge, so a tampered record can change only
63
+ * how soon due work runs.
64
+ */
65
+ readonly schedulerIntervalMinutes?: number;
56
66
  /** The tag rolled to: a version (`0.11.0`) or a channel tag (`latest`, `rc`). */
57
67
  readonly targetTag: string;
58
68
  /** The infrastructure contract version this CLI converges to (infra-contract.ts). */
@@ -75,9 +85,17 @@ export interface GcpUpgradeTarget {
75
85
  */
76
86
  export interface UpgradeCheckpoint {
77
87
  readonly backup?: SqlBackup;
78
- readonly priorRevisions?: Readonly<Record<RollSlug, string>>;
88
+ readonly priorRevisions?: PriorRevisions;
79
89
  }
80
90
 
91
+ /**
92
+ * The rollback target per service. The MCP-Auth-Proxy's is absent in a
93
+ * checkpoint a release before #1328 wrote; its planned revision is kept then.
94
+ */
95
+ export type PriorRevisions = Readonly<Record<"admin" | "gateway", string>> & {
96
+ readonly "mcp-auth-proxy"?: string;
97
+ };
98
+
81
99
  export interface GcpUpgradeDeps {
82
100
  readonly run: GcloudRunner;
83
101
  readonly reporter: UpgradeReporter;
@@ -165,6 +183,41 @@ export async function upgradeGcpInstallOutcome(
165
183
  r.error(images.error);
166
184
  return failed;
167
185
  }
186
+ // #1328 — the services the plan must find: the MCP-Auth-Proxy's account
187
+ // and, on an install whose stack predates it, the data gateway
188
+ // (gcp-firebase) or the proxy's own service (gcp). Additive, serving
189
+ // nothing yet, so the install keeps running as it is when this or
190
+ // anything after it fails. The gcp proxy runs the new release's image,
191
+ // so that image's signature is checked before it is deployed.
192
+ const proxyImage = images.images["mcp-auth-proxy"];
193
+ if (
194
+ target.provider === "gcp" &&
195
+ !target.skipVerify &&
196
+ !(await deps.verifyImages([{ slug: "mcp-auth-proxy", digest: proxyImage.digest }]))
197
+ ) {
198
+ sPre.failed("The MCP-Auth-Proxy image's signature did not verify");
199
+ return failed;
200
+ }
201
+ const ensured = await ensureReleaseServices(
202
+ {
203
+ provider: target.provider,
204
+ projectId,
205
+ region: target.region,
206
+ env: target.env,
207
+ domain: target.domain,
208
+ mcpAuthProxyImageRef: proxyImage.imageRef,
209
+ },
210
+ { run },
211
+ );
212
+ const ensureFailed = ensured.filter((o) => o.status === "failed");
213
+ if (ensureFailed.length > 0) {
214
+ sPre.failed("The services this release runs could not be set up. Nothing was changed.");
215
+ for (const o of ensureFailed) r.error(` FAILED: ${o.id}\n ${o.error ?? ""}`);
216
+ return failed;
217
+ }
218
+ for (const o of ensured.filter((o) => o.status === "applied")) {
219
+ r.info(` created: ${o.id}${o.detail ? ` (${o.detail})` : ""}`);
220
+ }
168
221
  const planned = await planAppRoll(
169
222
  {
170
223
  provider: target.provider,
@@ -229,6 +282,9 @@ export async function upgradeGcpInstallOutcome(
229
282
  const opsImageRef = await verifiedOpsImage(target, deps);
230
283
  const converge = startConverge(plan, target.ownerEmail, {
231
284
  opsImageRef,
285
+ ...(target.schedulerIntervalMinutes !== undefined
286
+ ? { schedulerIntervalMinutes: target.schedulerIntervalMinutes }
287
+ : {}),
232
288
  run,
233
289
  reporter: r,
234
290
  ...(deps.sleep ? { sleep: deps.sleep } : {}),
@@ -269,7 +325,9 @@ export async function upgradeGcpInstallOutcome(
269
325
  },
270
326
  );
271
327
  if (!rolled.ok) return { ok: false, rolledBack: rolled.rolledBack };
272
- r.success(`Upgrade to ${target.targetTag} complete (admin + gateway revisions serving).`);
328
+ r.success(
329
+ `Upgrade to ${target.targetTag} complete (admin, gateway and MCP-Auth-Proxy revisions serving).`,
330
+ );
273
331
  r.info(
274
332
  `Pre-upgrade backup: ${backup.backup.backupId} (restore: ${restoreCommand(backup.backup)})`,
275
333
  );
@@ -314,8 +372,8 @@ function withPriorRevisions(
314
372
  return {
315
373
  ...plan,
316
374
  services: plan.services.map((s) => {
317
- const before = recorded[s.slug];
318
- if (before === s.priorRevision) return s;
375
+ const before: string | undefined = recorded[s.slug];
376
+ if (before === undefined || before === s.priorRevision) return s;
319
377
  r.info(
320
378
  `Continuing an interrupted update: ${s.slug} goes back to ${before} (what served before it started) if the new release fails its checks.`,
321
379
  );
@@ -16,6 +16,13 @@
16
16
  import { mkdtempSync } from "node:fs";
17
17
  import { tmpdir } from "node:os";
18
18
  import { join } from "node:path";
19
+ import { isIpLiteral } from "@caelo-cms/shared";
20
+ import {
21
+ ensureCloudSqlVerifyFull,
22
+ liveDatabaseUrls,
23
+ moveServicesToDnsHost,
24
+ } from "./cloudsql-tls.js";
25
+ import { findSqlInstance } from "./gateway-credentials.js";
19
26
  import { gcloud } from "./gcloud.js";
20
27
  import { type GcloudRunner, realSleep } from "./gcloud-retry.js";
21
28
  import { GCP_STACK_ENV } from "./gcp-names.js";
@@ -23,10 +30,16 @@ import type { InstallMetadata } from "./install-state.js";
23
30
  import { readAdminConfig, runMigrationsViaCloudRunJob } from "./migration-runner.js";
24
31
  import { fetchVerifiedReleaseManifest } from "./release-manifest.js";
25
32
  import type { HttpFetch } from "./runtime-secrets.js";
33
+ import { DATABASE_URL_VARS } from "./stack-contract.js";
34
+ import { liveContainerEnv } from "./stack-converge.js";
26
35
  import { opsBucketName } from "./state-backend.js";
27
36
  import { acquireBucketStateLock, GcsStateStore, realSyncGcloud } from "./state-store.js";
28
- import type { AppRollPlan } from "./upgrade/app-roll.js";
29
- import { findServiceUrl } from "./upgrade/gcp-lookup.js";
37
+ import type { AppRollPlan, RollSlug } from "./upgrade/app-roll.js";
38
+ import {
39
+ describeServiceJson,
40
+ findServiceUrl,
41
+ resolveGcpResourceName,
42
+ } from "./upgrade/gcp-lookup.js";
30
43
  import { createIapHealthProbe } from "./upgrade/health.js";
31
44
  import { createReleaseImageForms } from "./upgrade/image-forms.js";
32
45
  import { type IamTester, restIamTester } from "./upgrader-iam-preflight.js";
@@ -58,6 +71,55 @@ export async function upgraderDatabaseHost(
58
71
  return cfg.databaseHost;
59
72
  }
60
73
 
74
+ /**
75
+ * #1338 — the database host for the converge job (#819), after making sure
76
+ * a verify-full connection to it can work. On an install whose services
77
+ * still dial the instance's IP it runs the converge's database TLS step
78
+ * (cloudsql-tls.ts: shared CA, private DNS name, services moved onto it).
79
+ * Stage 2 of the job runs it as a timeline step, over a queue opened on
80
+ * the IP (converge-db-trust.ts, #1373), then reconnects on the host this
81
+ * returns.
82
+ * The job runs as the install's `caelo-provisioner`, which holds
83
+ * `roles/cloudsql.admin` and `roles/dns.admin` (gcloud.ts PROVISIONER_ROLES).
84
+ * Idempotent: the converge's own step finds everything in shape.
85
+ */
86
+ export async function convergeDatabaseHost(
87
+ placement: Pick<UpgraderPlacement, "projectId" | "region">,
88
+ run: GcloudRunner = gcloud,
89
+ http?: HttpFetch,
90
+ ): Promise<string> {
91
+ const host = await upgraderDatabaseHost(placement, run);
92
+ if (!isIpLiteral(host)) return host;
93
+ const install = { projectId: placement.projectId, env: GCP_STACK_ENV };
94
+ const lookup = await findSqlInstance(install, { run });
95
+ if (lookup.status !== "found") {
96
+ throw new Error(`could not find the install's Cloud SQL instance (${lookup.status})`);
97
+ }
98
+ const deps = { run, ...(http ? { http } : {}) };
99
+ const tls = await ensureCloudSqlVerifyFull({ ...install, sqlInstance: lookup.name }, deps);
100
+ if (!tls.ok) throw new Error(`database TLS could not be set up: ${tls.error}`);
101
+ const services: { serviceName: string; liveUrls: Record<string, string> }[] = [];
102
+ for (const slug of ["admin", "gateway"] as const) {
103
+ const found = await resolveGcpResourceName(
104
+ run,
105
+ "run-service",
106
+ `caelo-${GCP_STACK_ENV}-${slug}`,
107
+ placement.projectId,
108
+ placement.region,
109
+ );
110
+ if (found.status !== "found") throw new Error(`could not find the ${slug} service`);
111
+ const json = await describeServiceJson(run, placement.projectId, placement.region, found.name);
112
+ if (!json) throw new Error(`could not read the ${slug} service's configuration`);
113
+ services.push({
114
+ serviceName: found.name,
115
+ liveUrls: liveDatabaseUrls(liveContainerEnv(json), DATABASE_URL_VARS[slug]),
116
+ });
117
+ }
118
+ const moved = await moveServicesToDnsHost(placement, services, tls.host, deps);
119
+ if (!moved.ok) throw new Error(moved.error);
120
+ return tls.host;
121
+ }
122
+
61
123
  /**
62
124
  * The IAP-backed deep health probe for `plan`'s install (the upgrader job;
63
125
  * the converge job, #819, too). `accessJustChanged`: a converge right
@@ -71,7 +133,7 @@ export async function healthProbeFor(
71
133
  context: { readonly accessJustChanged: boolean },
72
134
  ) {
73
135
  const { install } = plan;
74
- const urls: Partial<Record<"admin" | "gateway", string>> = {};
136
+ const urls: Partial<Record<RollSlug, string>> = {};
75
137
  for (const s of plan.services) {
76
138
  const url = await findServiceUrl(run, install.projectId, install.region, s.serviceName);
77
139
  if (!url) {
@@ -88,6 +150,7 @@ export async function healthProbeFor(
88
150
  domain: install.domain,
89
151
  adminServiceUrl: urls.admin as string,
90
152
  gatewayServiceUrl: urls.gateway as string,
153
+ mcpAuthProxyServiceUrl: urls["mcp-auth-proxy"] as string,
91
154
  },
92
155
  { run, http, sleep: realSleep, accessJustChanged: context.accessJustChanged },
93
156
  ),
@@ -73,6 +73,11 @@ function setupGrants(): Map<string, Set<string>> {
73
73
  `projects/${p}/serviceAccounts/caelo-production-gateway-sa@${p}.iam.gserviceaccount.com`,
74
74
  of("roles/iam.serviceAccountUser"),
75
75
  ],
76
+ // #1328 — the MCP-Auth-Proxy's revisions run as its own account.
77
+ [
78
+ `projects/${p}/serviceAccounts/caelo-production-mcp-proxy-sa@${p}.iam.gserviceaccount.com`,
79
+ of("roles/iam.serviceAccountUser"),
80
+ ],
76
81
  [
77
82
  `projects/${p}/serviceAccounts/caelo-production-upgrader@${p}.iam.gserviceaccount.com`,
78
83
  of("roles/iam.serviceAccountUser"),
@@ -154,6 +159,20 @@ describe("upgraderAccessChecks", () => {
154
159
  expect(preflightMissingReason(r.missing, UPGRADER_PREFLIGHT_VOICE)).toContain("capture job");
155
160
  });
156
161
 
162
+ it("#1328: the MCP-Auth-Proxy's account is checked, since every app-roll rolls the proxy", async () => {
163
+ const proxySa =
164
+ "projects/acme/serviceAccounts/caelo-production-mcp-proxy-sa@acme.iam.gserviceaccount.com";
165
+ const grants = setupGrants();
166
+ grants.delete(proxySa);
167
+ const r = await runIamPreflight(upgraderAccessChecks(TARGET), tester(grants));
168
+ expect(r.kind).toBe("missing");
169
+ if (r.kind !== "missing") return;
170
+ expect(r.missing.map((m) => [resourceName(m.check.resource), m.missing])).toEqual([
171
+ [proxySa, ["iam.serviceAccounts.actAs"]],
172
+ ]);
173
+ expect(preflightMissingReason(r.missing, UPGRADER_PREFLIGHT_VOICE)).toContain("MCP-Auth-Proxy");
174
+ });
175
+
157
176
  it("never needs a permission only a forbidden role would give (no compute, no IAM admin)", () => {
158
177
  const all = upgraderAccessChecks(TARGET).flatMap((c) => c.permissions);
159
178
  for (const p of all) {
@@ -54,6 +54,7 @@ import {
54
54
  auditServiceAccountEmail,
55
55
  captureServiceAccountEmail,
56
56
  gatewayServiceAccountEmail,
57
+ mcpAuthProxyServiceAccountEmail,
57
58
  mcpIapServiceAccountEmail,
58
59
  runServiceAccountEmail,
59
60
  upgraderJobName,
@@ -186,6 +187,11 @@ export function upgraderAccessChecks(target: {
186
187
  resource: sa(gatewayServiceAccountEmail(projectId, env)),
187
188
  permissions: ["iam.serviceAccounts.actAs"],
188
189
  },
190
+ {
191
+ need: "run the MCP-Auth-Proxy as its service account",
192
+ resource: sa(mcpAuthProxyServiceAccountEmail(projectId, env)),
193
+ permissions: ["iam.serviceAccounts.actAs"],
194
+ },
189
195
  {
190
196
  // #875 — every app-roll moves the audit job onto the new admin image
191
197
  // (upgrade/admin-image-jobs.ts), and upgrader-setup.ts grants actAs on
@@ -57,14 +57,16 @@ const revisionJson = (deployed: string | null, resolved: string) =>
57
57
  status: { imageDigest: `${MIRROR}@${resolved}` },
58
58
  });
59
59
 
60
- /** Serving revisions deployed as `admin` / `gateway` (index digests). */
60
+ /** Serving revisions deployed as `admin` / `gateway` (index digests); the proxy serves D("3"). */
61
61
  const servingAs = (admin: string, gateway: string): [RegExp, Answer] => [
62
62
  /^run revisions describe (\S+)/,
63
63
  (l) =>
64
64
  ok(
65
65
  l.includes("-admin-")
66
66
  ? revisionJson(admin, `sha256:${"e".repeat(64)}`)
67
- : revisionJson(gateway, `sha256:${"d".repeat(64)}`),
67
+ : l.includes(PROXY)
68
+ ? revisionJson(D("3"), P("3"))
69
+ : revisionJson(gateway, `sha256:${"d".repeat(64)}`),
68
70
  ),
69
71
  ];
70
72
 
@@ -79,13 +81,16 @@ const releaseTags = (slug: "admin" | "gateway", tags: Record<string, string>): [
79
81
  ];
80
82
 
81
83
  const ADMIN = "caelo-production-admin-aaa";
82
- const GATEWAY = "caelo-production-gateway-bbb";
84
+ // #1328 — gcp-firebase: the visitor gateway is the fixed-name data gateway,
85
+ // the MCP-Auth-Proxy the service that was the gateway (E13 a).
86
+ const GATEWAY = "caelo-production-data-gateway";
87
+ const PROXY = "caelo-production-gateway-bbb";
83
88
 
84
89
  const MANIFEST: ReleaseManifest = {
85
90
  manifestVersion: 1,
86
91
  version: "0.11.0",
87
92
  channel: "stable",
88
- digests: { admin: D("a"), gateway: D("b"), ops: D("c") },
93
+ digests: { admin: D("a"), gateway: D("b"), ops: D("c"), "mcp-auth-proxy": D("d") },
89
94
  minFromVersion: "0.10.0",
90
95
  infraContractVersion: 2,
91
96
  migrations: { contract: false, lastContractVersion: null, head: "0267_platform_updates" },
@@ -121,14 +126,23 @@ const META: InstallMetadata = {
121
126
  ownerEmail: "owner@acme.com",
122
127
  region: "europe-west1",
123
128
  createdAt: "2026-01-01T00:00:00Z",
124
- imageDigests: { admin: D("1"), gateway: D("2") },
129
+ imageDigests: { admin: D("1"), gateway: D("2"), "mcp-auth-proxy": D("3") },
125
130
  releaseVersion: "0.10.45",
126
131
  infraContractVersion: 2,
127
132
  };
128
133
 
129
- const manifestOf = (version: string, admin: string, gateway: string): ReleaseManifestResult => ({
134
+ const manifestOf = (
135
+ version: string,
136
+ admin: string,
137
+ gateway: string,
138
+ proxy = D("3"),
139
+ ): ReleaseManifestResult => ({
130
140
  ok: true,
131
- manifest: { ...MANIFEST, version, digests: { admin, gateway, ops: D("c") } },
141
+ manifest: {
142
+ ...MANIFEST,
143
+ version,
144
+ digests: { admin, gateway, ops: D("c"), "mcp-auth-proxy": proxy },
145
+ },
132
146
  });
133
147
 
134
148
  const serviceJson = (image: string, env: unknown[]) =>
@@ -141,11 +155,13 @@ const ADMIN_JSON = serviceJson("registry/admin@sha256:old", [
141
155
  { name: "CAELO_FIREBASE_SITE", value: "caelo-production-site-abc123" },
142
156
  {
143
157
  name: "ADMIN_DATABASE_URL",
144
- value: "postgresql://admin_role@10.20.0.3:5432/cms_admin?sslmode=require",
158
+ value:
159
+ "postgresql://admin_role@db1.p-label.europe-west1.sql-psa.goog:5432/cms_admin?sslmode=verify-full&sslrootcert=/etc/caelo/db-ca/cloudsql-global.pem",
145
160
  },
146
161
  secretRef("ADMIN_DATABASE_PASSWORD", "postgres-password"),
147
162
  ]);
148
163
  const GATEWAY_JSON = serviceJson("registry/gateway@sha256:old", []);
164
+ const PROXY_JSON = serviceJson("registry/mcp-auth-proxy@sha256:old", []);
149
165
 
150
166
  type Answer = GcloudResult | ((line: string) => GcloudResult);
151
167
 
@@ -153,15 +169,23 @@ function fakeGcloud(events: string[], overrides: [RegExp, Answer][] = []) {
153
169
  const rules: [RegExp, Answer][] = [
154
170
  ...overrides,
155
171
  [/^run services list .*caelo-production-admin/, ok(`${ADMIN}\n`)],
156
- [/^run services list .*caelo-production-gateway/, ok(`${GATEWAY}\n`)],
172
+ [/^run services list .*caelo-production-data-gateway/, ok(`${GATEWAY}\n`)],
173
+ [/^run services list .*caelo-production-gateway/, ok(`${PROXY}\n`)],
157
174
  [/describe (\S+) .*revisionName/, (l) => ok(`${l.split(" ")[3]}-00001-old\n`)],
158
175
  [new RegExp(`describe ${ADMIN} .*--format=json`), ok(ADMIN_JSON)],
159
176
  [new RegExp(`describe ${GATEWAY} .*--format=json`), ok(GATEWAY_JSON)],
177
+ [new RegExp(`describe ${PROXY} .*--format=json`), ok(PROXY_JSON)],
160
178
  [/describe (\S+) .*status\.url/, (l) => ok(`https://${l.split(" ")[3]}-xyz.a.run.app\n`)],
161
179
  [
162
180
  /^run revisions describe (\S+)/,
163
181
  (l) =>
164
- ok(l.includes("-admin-") ? revisionJson(D("1"), P("1")) : revisionJson(D("2"), P("2"))),
182
+ ok(
183
+ l.includes("-admin-")
184
+ ? revisionJson(D("1"), P("1"))
185
+ : l.includes(PROXY)
186
+ ? revisionJson(D("3"), P("3"))
187
+ : revisionJson(D("2"), P("2")),
188
+ ),
165
189
  ],
166
190
  [/^sql instances list/, ok("caelo-production-pg-abc\n")],
167
191
  [/^sql backups list/, ok("1760000000000\n")],
@@ -307,9 +331,11 @@ function fakeImageForms(order: string[], down: boolean): ReleaseImageForms {
307
331
  return {
308
332
  ofIndex,
309
333
  of: async (m, slug) => {
334
+ const index = m.digests[slug];
335
+ if (!index) return { ok: false, transient: false, message: `no ${slug} image` };
310
336
  const signed = m.platformDigests?.[slug];
311
- if (signed) return { ok: true, digests: [m.digests[slug], signed], amd64: signed };
312
- return ofIndex(slug, m.digests[slug]);
337
+ if (signed) return { ok: true, digests: [index, signed], amd64: signed };
338
+ return ofIndex(slug, index);
313
339
  },
314
340
  };
315
341
  }
@@ -393,6 +419,21 @@ describe("runUpgraderJob", () => {
393
419
  ]);
394
420
  });
395
421
 
422
+ it("#1328: a target manifest without an MCP-Auth-Proxy image stops before anything changes", async () => {
423
+ const { "mcp-auth-proxy": _none, ...digests } = MANIFEST.digests;
424
+ const { deps, order, q, written } = setup({
425
+ manifest: { ok: true, manifest: { ...MANIFEST, digests } },
426
+ });
427
+ const r = await runUpgraderJob(deps);
428
+ expect(r.status).toBe("failed");
429
+ expect(q.end()?.codes).toEqual(["manifest"]);
430
+ expect(q.end()?.reason).toContain("names no mcp-auth-proxy image");
431
+ expect(order.some((l) => l.startsWith("gcloud sql backups create"))).toBe(false);
432
+ expect(order.some((l) => l.startsWith("migrate "))).toBe(false);
433
+ expect(order.some((l) => l.startsWith("gcloud run services update"))).toBe(false);
434
+ expect(written).toEqual([]);
435
+ });
436
+
396
437
  it("#815: decides first, locks right before the backup, holds the lock to the end", async () => {
397
438
  const ok = setup();
398
439
  await runUpgraderJob(ok.deps);
@@ -429,6 +470,7 @@ describe("runUpgraderJob", () => {
429
470
  "iam projects/acme/serviceAccounts/caelo-production-audit@acme.iam.gserviceaccount.com",
430
471
  "iam projects/acme/serviceAccounts/caelo-production-capture@acme.iam.gserviceaccount.com",
431
472
  "iam projects/acme/serviceAccounts/caelo-production-gateway-sa@acme.iam.gserviceaccount.com",
473
+ "iam projects/acme/serviceAccounts/caelo-production-mcp-proxy-sa@acme.iam.gserviceaccount.com",
432
474
  "iam projects/acme/serviceAccounts/caelo-production-run-sa@acme.iam.gserviceaccount.com",
433
475
  "iam projects/acme/serviceAccounts/caelo-production-upgrader@acme.iam.gserviceaccount.com",
434
476
  "iam projects/caelo-website/locations/europe-west1/repositories/caelo-cms-images",
@@ -616,12 +658,21 @@ describe("runUpgraderJob", () => {
616
658
 
617
659
  it("a revision deployed by tag matches by the digest Cloud Run resolved", async () => {
618
660
  const { deps, written } = setup({
619
- meta: { ...META, imageDigests: { admin: P("1"), gateway: D("2") } },
661
+ meta: {
662
+ ...META,
663
+ imageDigests: { admin: P("1"), gateway: D("2"), "mcp-auth-proxy": D("3") },
664
+ },
620
665
  gcloud: [
621
666
  [
622
667
  /^run revisions describe (\S+)/,
623
668
  (l) =>
624
- ok(l.includes("-admin-") ? revisionJson(null, P("1")) : revisionJson(D("2"), P("2"))),
669
+ ok(
670
+ l.includes("-admin-")
671
+ ? revisionJson(null, P("1"))
672
+ : l.includes(PROXY)
673
+ ? revisionJson(D("3"), P("3"))
674
+ : revisionJson(D("2"), P("2")),
675
+ ),
625
676
  ],
626
677
  ],
627
678
  });
@@ -664,14 +715,20 @@ describe("runUpgraderJob", () => {
664
715
 
665
716
  it("#815: drift + a stop verdict: the records are corrected under the lock, the verdict stands", async () => {
666
717
  const drift = {
667
- meta: { ...META, imageDigests: { admin: D("9"), gateway: D("2") }, infraContractVersion: 1 },
718
+ meta: {
719
+ ...META,
720
+ imageDigests: { admin: D("9"), gateway: D("2"), "mcp-auth-proxy": D("3") },
721
+ infraContractVersion: 1,
722
+ },
668
723
  manifests: { "0.10.45": manifestOf("0.10.45", D("1"), D("2")) },
669
724
  };
670
725
  const free = setup(drift);
671
726
  expect((await runUpgraderJob(free.deps)).status).toBe("needs_cloud_shell");
672
727
  expect(free.q.end()?.codes).toEqual(["infra-contract"]);
673
728
  expect(free.written).toEqual([
674
- expect.objectContaining({ imageDigests: { admin: D("1"), gateway: D("2") } }),
729
+ expect.objectContaining({
730
+ imageDigests: { admin: D("1"), gateway: D("2"), "mcp-auth-proxy": D("3") },
731
+ }),
675
732
  ]);
676
733
  expect(free.order.indexOf("lock")).toBeGreaterThan(step(free.order, /^manifest 0\.11\.0/));
677
734
  expect(free.order).toContain("unlock");
@@ -814,7 +871,8 @@ describe("runUpgraderJob", () => {
814
871
  const servingPlatform = (admin: string, gateway: string): [RegExp, Answer] => [
815
872
  /^run revisions describe (\S+)/,
816
873
  (l) => {
817
- const d = l.includes("-admin-") ? admin : gateway;
874
+ // The MCP-Auth-Proxy serves its recorded index D("3") throughout.
875
+ const d = l.includes("-admin-") ? admin : l.includes(PROXY) ? D("3") : gateway;
818
876
  return ok(revisionJson(d, d));
819
877
  },
820
878
  ];
@@ -824,12 +882,17 @@ describe("runUpgraderJob", () => {
824
882
  const v064 = {
825
883
  ...MANIFEST,
826
884
  version: "0.10.64",
827
- digests: { admin: ADMIN_INDEX, gateway: GATEWAY_INDEX, ops: D("c") },
885
+ digests: {
886
+ admin: ADMIN_INDEX,
887
+ gateway: GATEWAY_INDEX,
888
+ ops: D("c"),
889
+ "mcp-auth-proxy": D("3"),
890
+ },
828
891
  };
829
892
  const { deps, q, written } = setup({
830
893
  meta: {
831
894
  ...META,
832
- imageDigests: { admin: ADMIN_INDEX, gateway: GATEWAY_INDEX },
895
+ imageDigests: { admin: ADMIN_INDEX, gateway: GATEWAY_INDEX, "mcp-auth-proxy": D("3") },
833
896
  releaseVersion: "0.10.64",
834
897
  },
835
898
  manifests: { "0.10.64": { ok: true, manifest: v064 } },
@@ -839,7 +902,7 @@ describe("runUpgraderJob", () => {
839
902
  expect((await runUpgraderJob(deps)).status).toBe("succeeded");
840
903
  expect(written[0]).toMatchObject({
841
904
  releaseVersion: "0.10.64",
842
- imageDigests: { admin: ADMIN_INDEX, gateway: GATEWAY_INDEX },
905
+ imageDigests: { admin: ADMIN_INDEX, gateway: GATEWAY_INDEX, "mcp-auth-proxy": D("3") },
843
906
  platformDigests: { admin: ADMIN_AMD64, gateway: GATEWAY_AMD64 },
844
907
  });
845
908
  expect(q.events.some((e) => e.message.startsWith("0.10.64 → 0.11.0"))).toBe(true);
@@ -861,6 +924,7 @@ describe("runUpgraderJob", () => {
861
924
  expect(order.filter((l) => l.startsWith("index "))).toEqual([
862
925
  `index admin@${D("a")}`,
863
926
  `index gateway@${D("b")}`,
927
+ `index mcp-auth-proxy@${D("d")}`,
864
928
  ]);
865
929
  expect(q.events.some((e) => e.message.includes("refreshed its install records"))).toBe(false);
866
930
  });
@@ -868,14 +932,14 @@ describe("runUpgraderJob", () => {
868
932
  it("the roll records the new release's platform forms, signed ones first", async () => {
869
933
  const signed = {
870
934
  ...MANIFEST,
871
- platformDigests: { admin: P("x"), gateway: P("y"), ops: P("z") },
935
+ platformDigests: { admin: P("x"), gateway: P("y"), ops: P("z"), "mcp-auth-proxy": P("w") },
872
936
  };
873
937
  const { deps, order, written } = setup({ manifest: { ok: true, manifest: signed } });
874
938
  expect((await runUpgraderJob(deps)).status).toBe("succeeded");
875
939
  expect(written.at(-1)).toMatchObject({
876
940
  releaseVersion: "0.11.0",
877
- imageDigests: { admin: D("a"), gateway: D("b") },
878
- platformDigests: { admin: P("x"), gateway: P("y") },
941
+ imageDigests: { admin: D("a"), gateway: D("b"), "mcp-auth-proxy": D("d") },
942
+ platformDigests: { admin: P("x"), gateway: P("y"), "mcp-auth-proxy": P("w") },
879
943
  });
880
944
  expect(order.some((l) => l.startsWith("index "))).toBe(false);
881
945
  });
@@ -886,7 +950,11 @@ describe("runUpgraderJob", () => {
886
950
  imageForms: "down",
887
951
  });
888
952
  expect((await runUpgraderJob(deps)).status).toBe("succeeded");
889
- expect(written.at(-1)?.imageDigests).toEqual({ admin: D("a"), gateway: D("b") });
953
+ expect(written.at(-1)?.imageDigests).toEqual({
954
+ admin: D("a"),
955
+ gateway: D("b"),
956
+ "mcp-auth-proxy": D("d"),
957
+ });
890
958
  expect(written.at(-1)?.platformDigests).toBeUndefined();
891
959
  expect(q.events.some((e) => e.level === "warn" && e.message.includes("linux/amd64"))).toBe(
892
960
  true,
@@ -995,7 +1063,9 @@ describe("runUpgraderJob", () => {
995
1063
  manifests: { "0.10.45": manifestOf("0.10.45", D("1"), D("2")) },
996
1064
  });
997
1065
  expect((await runUpgraderJob(deps)).status).toBe("needs_cloud_shell");
998
- expect(q.end()?.reason).toContain("The admin serves v0.10.45's image, the gateway does not");
1066
+ expect(q.end()?.reason).toContain(
1067
+ "The admin and the MCP-Auth-Proxy serve v0.10.45's image, the gateway does not",
1068
+ );
999
1069
  expect(written).toEqual([]);
1000
1070
  });
1001
1071
 
@@ -1021,8 +1091,12 @@ describe("runUpgraderJob", () => {
1021
1091
  expect((await runUpgraderJob(deps)).status).toBe("needs_cloud_shell");
1022
1092
  const end = q.end();
1023
1093
  expect(end?.codes).toEqual(["install-state-drift"]);
1024
- expect(end?.reason).toContain("The gateway serves v0.10.45's image, the admin does not");
1025
- expect(end?.reason).toContain("the admin serves v0.10.44's image, the gateway does not");
1094
+ expect(end?.reason).toContain(
1095
+ "The gateway and the MCP-Auth-Proxy serve v0.10.45's image, the admin does not",
1096
+ );
1097
+ expect(end?.reason).toContain(
1098
+ "the admin and the MCP-Auth-Proxy serve v0.10.44's image, the gateway does not",
1099
+ );
1026
1100
  expect(written).toEqual([]);
1027
1101
  });
1028
1102