@caelo-cms/provisioning 0.10.53 → 0.10.57

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 (244) hide show
  1. package/dist/agent-db.js +272 -128
  2. package/dist/audit-job-setup.d.ts +69 -0
  3. package/dist/audit-job-setup.d.ts.map +1 -0
  4. package/dist/audit-job-setup.js +317 -0
  5. package/dist/audit-job-setup.js.map +1 -0
  6. package/dist/caddy.d.ts +9 -0
  7. package/dist/caddy.d.ts.map +1 -1
  8. package/dist/caddy.js +38 -0
  9. package/dist/caddy.js.map +1 -1
  10. package/dist/cli-routing.js +1 -1
  11. package/dist/cli-routing.js.map +1 -1
  12. package/dist/cli.js +36 -9
  13. package/dist/cli.js.map +1 -1
  14. package/dist/compose.js +5 -0
  15. package/dist/compose.js.map +1 -1
  16. package/dist/converge-checkpoint.d.ts +66 -0
  17. package/dist/converge-checkpoint.d.ts.map +1 -0
  18. package/dist/converge-checkpoint.js +61 -0
  19. package/dist/converge-checkpoint.js.map +1 -0
  20. package/dist/converge-job-gcp.d.ts +46 -0
  21. package/dist/converge-job-gcp.d.ts.map +1 -0
  22. package/dist/converge-job-gcp.js +368 -0
  23. package/dist/converge-job-gcp.js.map +1 -0
  24. package/dist/converge-job-setup.d.ts +62 -0
  25. package/dist/converge-job-setup.d.ts.map +1 -0
  26. package/dist/converge-job-setup.js +228 -0
  27. package/dist/converge-job-setup.js.map +1 -0
  28. package/dist/converge-job.d.ts +141 -0
  29. package/dist/converge-job.d.ts.map +1 -0
  30. package/dist/converge-job.js +325 -0
  31. package/dist/converge-job.js.map +1 -0
  32. package/dist/gcp-names.d.ts +25 -1
  33. package/dist/gcp-names.d.ts.map +1 -1
  34. package/dist/gcp-names.js +36 -0
  35. package/dist/gcp-names.js.map +1 -1
  36. package/dist/index.d.ts +2 -0
  37. package/dist/index.d.ts.map +1 -1
  38. package/dist/index.js +2 -0
  39. package/dist/index.js.map +1 -1
  40. package/dist/infra-contract.d.ts +1 -1
  41. package/dist/infra-contract.d.ts.map +1 -1
  42. package/dist/infra-contract.js +28 -1
  43. package/dist/infra-contract.js.map +1 -1
  44. package/dist/install-job.d.ts +11 -3
  45. package/dist/install-job.d.ts.map +1 -1
  46. package/dist/install-job.js +22 -12
  47. package/dist/install-job.js.map +1 -1
  48. package/dist/lifecycle.d.ts +13 -1
  49. package/dist/lifecycle.d.ts.map +1 -1
  50. package/dist/lifecycle.js +115 -58
  51. package/dist/lifecycle.js.map +1 -1
  52. package/dist/operator-access.d.ts.map +1 -1
  53. package/dist/operator-access.js +13 -3
  54. package/dist/operator-access.js.map +1 -1
  55. package/dist/private-file.d.ts +26 -0
  56. package/dist/private-file.d.ts.map +1 -0
  57. package/dist/private-file.js +49 -0
  58. package/dist/private-file.js.map +1 -0
  59. package/dist/release-contract.d.ts +3 -2
  60. package/dist/release-contract.d.ts.map +1 -1
  61. package/dist/release-contract.js +8 -2
  62. package/dist/release-contract.js.map +1 -1
  63. package/dist/release-manifest.d.ts +2 -0
  64. package/dist/release-manifest.d.ts.map +1 -1
  65. package/dist/release-manifest.js.map +1 -1
  66. package/dist/remote-mcp-edge.d.ts +114 -0
  67. package/dist/remote-mcp-edge.d.ts.map +1 -0
  68. package/dist/remote-mcp-edge.js +116 -0
  69. package/dist/remote-mcp-edge.js.map +1 -0
  70. package/dist/remote-mcp.d.ts +117 -0
  71. package/dist/remote-mcp.d.ts.map +1 -0
  72. package/dist/remote-mcp.js +379 -0
  73. package/dist/remote-mcp.js.map +1 -0
  74. package/dist/runtime-secrets.d.ts +12 -0
  75. package/dist/runtime-secrets.d.ts.map +1 -1
  76. package/dist/runtime-secrets.js +32 -4
  77. package/dist/runtime-secrets.js.map +1 -1
  78. package/dist/seal-key-rotation.d.ts +38 -0
  79. package/dist/seal-key-rotation.d.ts.map +1 -0
  80. package/dist/seal-key-rotation.js +206 -0
  81. package/dist/seal-key-rotation.js.map +1 -0
  82. package/dist/stack-contract.d.ts +175 -3
  83. package/dist/stack-contract.d.ts.map +1 -1
  84. package/dist/stack-contract.js +183 -3
  85. package/dist/stack-contract.js.map +1 -1
  86. package/dist/stack-converge.d.ts +38 -1
  87. package/dist/stack-converge.d.ts.map +1 -1
  88. package/dist/stack-converge.js +74 -4
  89. package/dist/stack-converge.js.map +1 -1
  90. package/dist/state-lock-lease.d.ts +142 -0
  91. package/dist/state-lock-lease.d.ts.map +1 -0
  92. package/dist/state-lock-lease.js +347 -0
  93. package/dist/state-lock-lease.js.map +1 -0
  94. package/dist/state-store.d.ts +66 -9
  95. package/dist/state-store.d.ts.map +1 -1
  96. package/dist/state-store.js +249 -30
  97. package/dist/state-store.js.map +1 -1
  98. package/dist/tar-entries.d.ts.map +1 -1
  99. package/dist/tar-entries.js +84 -9
  100. package/dist/tar-entries.js.map +1 -1
  101. package/dist/upgrade/app-roll.d.ts +36 -7
  102. package/dist/upgrade/app-roll.d.ts.map +1 -1
  103. package/dist/upgrade/app-roll.js +88 -19
  104. package/dist/upgrade/app-roll.js.map +1 -1
  105. package/dist/upgrade/audit-job-roll.d.ts +38 -0
  106. package/dist/upgrade/audit-job-roll.d.ts.map +1 -0
  107. package/dist/upgrade/audit-job-roll.js +43 -0
  108. package/dist/upgrade/audit-job-roll.js.map +1 -0
  109. package/dist/upgrade/converge.d.ts.map +1 -1
  110. package/dist/upgrade/converge.js +141 -8
  111. package/dist/upgrade/converge.js.map +1 -1
  112. package/dist/upgrade/gcp-lookup.d.ts +27 -10
  113. package/dist/upgrade/gcp-lookup.d.ts.map +1 -1
  114. package/dist/upgrade/gcp-lookup.js +46 -7
  115. package/dist/upgrade/gcp-lookup.js.map +1 -1
  116. package/dist/upgrade/record-rolled.d.ts +40 -0
  117. package/dist/upgrade/record-rolled.d.ts.map +1 -0
  118. package/dist/upgrade/record-rolled.js +46 -0
  119. package/dist/upgrade/record-rolled.js.map +1 -0
  120. package/dist/upgrade/upgrade.d.ts +45 -1
  121. package/dist/upgrade/upgrade.d.ts.map +1 -1
  122. package/dist/upgrade/upgrade.js +60 -17
  123. package/dist/upgrade/upgrade.js.map +1 -1
  124. package/dist/upgrader-assess.d.ts +15 -1
  125. package/dist/upgrader-assess.d.ts.map +1 -1
  126. package/dist/upgrader-assess.js +36 -6
  127. package/dist/upgrader-assess.js.map +1 -1
  128. package/dist/upgrader-gcp.d.ts +15 -0
  129. package/dist/upgrader-gcp.d.ts.map +1 -1
  130. package/dist/upgrader-gcp.js +42 -7
  131. package/dist/upgrader-gcp.js.map +1 -1
  132. package/dist/upgrader-iam-preflight.d.ts +123 -0
  133. package/dist/upgrader-iam-preflight.d.ts.map +1 -0
  134. package/dist/upgrader-iam-preflight.js +414 -0
  135. package/dist/upgrader-iam-preflight.js.map +1 -0
  136. package/dist/upgrader-job.d.ts +33 -9
  137. package/dist/upgrader-job.d.ts.map +1 -1
  138. package/dist/upgrader-job.js +169 -27
  139. package/dist/upgrader-job.js.map +1 -1
  140. package/dist/upgrader-records.d.ts +58 -0
  141. package/dist/upgrader-records.d.ts.map +1 -0
  142. package/dist/upgrader-records.js +87 -0
  143. package/dist/upgrader-records.js.map +1 -0
  144. package/dist/upgrader-reporter.d.ts +3 -2
  145. package/dist/upgrader-reporter.d.ts.map +1 -1
  146. package/dist/upgrader-reporter.js +10 -2
  147. package/dist/upgrader-reporter.js.map +1 -1
  148. package/dist/upgrader-setup.d.ts +25 -3
  149. package/dist/upgrader-setup.d.ts.map +1 -1
  150. package/dist/upgrader-setup.js +132 -14
  151. package/dist/upgrader-setup.js.map +1 -1
  152. package/dist/wizards/gcp-cost.d.ts.map +1 -1
  153. package/dist/wizards/gcp-cost.js +14 -6
  154. package/dist/wizards/gcp-cost.js.map +1 -1
  155. package/dist/wizards/gcp-pulumi.d.ts +6 -0
  156. package/dist/wizards/gcp-pulumi.d.ts.map +1 -1
  157. package/dist/wizards/gcp-pulumi.js +7 -0
  158. package/dist/wizards/gcp-pulumi.js.map +1 -1
  159. package/dist/wizards/gcp.d.ts.map +1 -1
  160. package/dist/wizards/gcp.js +130 -4
  161. package/dist/wizards/gcp.js.map +1 -1
  162. package/package.json +4 -4
  163. package/src/agent/run.test.ts +15 -1
  164. package/src/audit-job-setup.test.ts +234 -0
  165. package/src/audit-job-setup.ts +360 -0
  166. package/src/caddy-redirects.docker.test.ts +19 -6
  167. package/src/caddy.test.ts +30 -0
  168. package/src/caddy.ts +43 -0
  169. package/src/cli-init-validation.test.ts +44 -0
  170. package/src/cli-routing.ts +1 -1
  171. package/src/cli.ts +40 -8
  172. package/src/compose.ts +5 -0
  173. package/src/converge-checkpoint.ts +75 -0
  174. package/src/converge-entrypoint.test.ts +213 -0
  175. package/src/converge-job-gcp.test.ts +222 -0
  176. package/src/converge-job-gcp.ts +443 -0
  177. package/src/converge-job-setup.test.ts +152 -0
  178. package/src/converge-job-setup.ts +268 -0
  179. package/src/converge-job.test.ts +486 -0
  180. package/src/converge-job.ts +542 -0
  181. package/src/gateway-env-isolation.test.ts +8 -1
  182. package/src/gcp-names.ts +43 -1
  183. package/src/gcp-url-map.test.ts +9 -1
  184. package/src/index.ts +8 -0
  185. package/src/infra-contract.test.ts +7 -4
  186. package/src/infra-contract.ts +28 -1
  187. package/src/install-job.test.ts +3 -1
  188. package/src/install-job.ts +31 -14
  189. package/src/lifecycle.ts +137 -71
  190. package/src/operator-access.test.ts +14 -0
  191. package/src/operator-access.ts +13 -3
  192. package/src/private-file.test.ts +80 -0
  193. package/src/private-file.ts +50 -0
  194. package/src/release-contract.ts +8 -2
  195. package/src/release-manifest.test.ts +42 -0
  196. package/src/release-manifest.ts +2 -0
  197. package/src/remote-mcp-edge.ts +137 -0
  198. package/src/remote-mcp.test.ts +517 -0
  199. package/src/remote-mcp.ts +460 -0
  200. package/src/runtime-secrets.test.ts +31 -6
  201. package/src/runtime-secrets.ts +41 -3
  202. package/src/seal-key-rotation.test.ts +131 -0
  203. package/src/seal-key-rotation.ts +258 -0
  204. package/src/stack-contract-parity.test.ts +20 -3
  205. package/src/stack-contract.test.ts +34 -2
  206. package/src/stack-contract.ts +305 -3
  207. package/src/stack-converge-secrets.test.ts +3 -0
  208. package/src/stack-converge.test.ts +21 -5
  209. package/src/stack-converge.ts +101 -7
  210. package/src/state-attach.test.ts +7 -1
  211. package/src/state-lock-lease.test.ts +302 -0
  212. package/src/state-lock-lease.ts +423 -0
  213. package/src/state-lock.test.ts +568 -0
  214. package/src/state-store.ts +347 -27
  215. package/src/tar-entries.test.ts +149 -7
  216. package/src/tar-entries.ts +90 -9
  217. package/src/upgrade/app-roll.ts +135 -21
  218. package/src/upgrade/audit-job-roll.ts +68 -0
  219. package/src/upgrade/converge.ts +170 -7
  220. package/src/upgrade/gcp-lookup.test.ts +80 -0
  221. package/src/upgrade/gcp-lookup.ts +65 -8
  222. package/src/upgrade/record-rolled.test.ts +105 -0
  223. package/src/upgrade/record-rolled.ts +91 -0
  224. package/src/upgrade/upgrade.test.ts +279 -5
  225. package/src/upgrade/upgrade.ts +120 -24
  226. package/src/upgrader-assess.test.ts +93 -3
  227. package/src/upgrader-assess.ts +58 -9
  228. package/src/upgrader-gcp.ts +47 -8
  229. package/src/upgrader-iam-preflight.test.ts +393 -0
  230. package/src/upgrader-iam-preflight.ts +510 -0
  231. package/src/upgrader-job.test.ts +587 -15
  232. package/src/upgrader-job.ts +237 -53
  233. package/src/upgrader-records.ts +151 -0
  234. package/src/upgrader-reporter.test.ts +6 -0
  235. package/src/upgrader-reporter.ts +7 -2
  236. package/src/upgrader-setup.test.ts +4 -3
  237. package/src/upgrader-setup.ts +137 -13
  238. package/src/wizards/gcp-cost.ts +15 -6
  239. package/src/wizards/gcp-pulumi.ts +7 -0
  240. package/src/wizards/gcp.ts +162 -4
  241. package/stacks/aws/index.ts +18 -0
  242. package/stacks/azure/index.ts +18 -0
  243. package/stacks/gcp/index.ts +59 -7
  244. package/stacks/gcp-firebase/index.ts +50 -6
@@ -0,0 +1,317 @@
1
+ // SPDX-License-Identifier: MPL-2.0
2
+ /**
3
+ * #875 — set up (and converge) the parallel quality-audit job on a Google
4
+ * Cloud install (`gcp`, `gcp-firebase`). Run by the wizard after `pulumi up`
5
+ * and by `cms-provision upgrade`'s converge, both as the operator's own
6
+ * gcloud identity. App-roll moves the job's image with the admin's
7
+ * (upgrade/app-roll.ts), because the job runs the admin image.
8
+ *
9
+ * Why a separate job at all (CLAUDE.md §11.B Tier 5 exception): Lighthouse
10
+ * is CPU-bound and simulates the CPU slowdown from observed task durations,
11
+ * so a run that shares a CPU with another run or with the editor's chat
12
+ * measures that interference too. One task per run measures the page alone,
13
+ * and all runs finish in about the time of one.
14
+ *
15
+ * Why the CLI owns it and not the stacks: `upgrade` never runs Pulumi, so
16
+ * existing installs get the job only from here, and a bucket or job both
17
+ * Pulumi and `upgrade` create would collide on the next wizard re-run (the
18
+ * same reasoning as operator-access.ts and upgrader-setup.ts).
19
+ *
20
+ * Trust boundary. The job's service account, `caelo-<env>-audit`, holds:
21
+ *
22
+ * - `roles/storage.objectCreator` on the audit results bucket — create
23
+ * only: it can neither read results back nor overwrite one;
24
+ * - on `gcp` only, `roles/storage.objectViewer` on the staging and static
25
+ * buckets: it serves the staged build to its own browser, as the admin's
26
+ * audit does (admin-core quality/staged-origin.ts). gcp-firebase audits
27
+ * the public preview channel and needs no bucket;
28
+ * - `roles/logging.logWriter`.
29
+ *
30
+ * Nothing else: no Secret Manager, no Cloud SQL, and no VPC egress, so the
31
+ * database's private IP is not even routable from a task. Plugin data on a
32
+ * page comes the way any visitor gets it: the task's browser calls the
33
+ * site's public `/api/*` (the gateway, behind Cloud Armor) over the
34
+ * internet. The admin's run
35
+ * SA may run the job WITH overrides (`roles/run.jobsExecutorWithOverrides`:
36
+ * it passes each audit's batch of orders), read its executions
37
+ * (`roles/run.viewer`) and read the results bucket. Unlike the
38
+ * operator-access and upgrader jobs, overrides are acceptable here: a
39
+ * compromised admin can make the job load other URLs, but the job holds
40
+ * nothing worth taking. {@link AUDIT_FORBIDDEN_ROLES} names that line; the
41
+ * tests assert no grant crosses it.
42
+ */
43
+ import { mkdtempSync, rmSync, writeFileSync } from "node:fs";
44
+ import { tmpdir } from "node:os";
45
+ import { join } from "node:path";
46
+ import { gcloud as defaultGcloud } from "./gcloud.js";
47
+ import { realSleep, runWithRetry } from "./gcloud-retry.js";
48
+ import { auditJobName, auditServiceAccountEmail, auditServiceAccountId, gcpBucketName, runServiceAccountEmail, } from "./gcp-names.js";
49
+ import { readAdminConfig } from "./migration-runner.js";
50
+ import { describeAuditJobImage } from "./upgrade/audit-job-roll.js";
51
+ /** Where the admin image ships the task's entry point. */
52
+ export const AUDIT_TASK_SCRIPT = "/app/packages/admin-core/src/quality/audit-task.ts";
53
+ /** How many audit tasks run at once by default: 5 pages x 3 runs. */
54
+ export const AUDIT_PARALLELISM_DEFAULT = 15;
55
+ /**
56
+ * Per task: container start, browser start, CPU benchmark and one
57
+ * Lighthouse run. Matches admin-core `parallelAuditTimeoutMs`, which the
58
+ * admin also passes per execution.
59
+ */
60
+ const TASK_TIMEOUT = "5m";
61
+ /**
62
+ * Roles the audit job's identity must never hold anywhere. Prefix matches
63
+ * (`roles/secretmanager.` = every Secret Manager role).
64
+ */
65
+ export const AUDIT_FORBIDDEN_ROLES = [
66
+ "roles/owner",
67
+ "roles/editor",
68
+ "roles/run.",
69
+ "roles/iam.",
70
+ "roles/secretmanager.",
71
+ "roles/cloudsql.",
72
+ "roles/compute.",
73
+ "roles/resourcemanager.",
74
+ "roles/storage.admin",
75
+ "roles/storage.objectAdmin",
76
+ "roles/storage.objectUser",
77
+ ];
78
+ /** Delete results after a day: the admin reads them within minutes. */
79
+ export const AUDIT_RESULTS_LIFECYCLE = {
80
+ rule: [{ action: { type: "Delete" }, condition: { age: 1 } }],
81
+ };
82
+ /** Ensure the audit job and everything it needs. Never throws for a gcloud failure. */
83
+ export async function ensureAuditJob(target, deps = {}) {
84
+ const run = deps.run ?? defaultGcloud;
85
+ const sleep = deps.sleep ?? realSleep;
86
+ const retry = (args) => runWithRetry(run, sleep, args);
87
+ const { projectId, region, env, provider } = target;
88
+ const project = `--project=${projectId}`;
89
+ const sa = auditServiceAccountEmail(projectId, env);
90
+ const member = `serviceAccount:${sa}`;
91
+ const runSaMember = `serviceAccount:${runServiceAccountEmail(projectId, env)}`;
92
+ const job = auditJobName(env);
93
+ const results = gcpBucketName(projectId, env, "audit-results");
94
+ const resultsUrl = `gs://${results}`;
95
+ const done = [];
96
+ const fail = (step, r) => ({
97
+ ok: false,
98
+ done,
99
+ error: `${step}: ${r.stderr.trim()}`,
100
+ });
101
+ if (!/@sha256:[0-9a-f]{64}$/.test(target.adminImageRef)) {
102
+ return {
103
+ ok: false,
104
+ done,
105
+ error: `the audit job's image must be pinned by digest, got ${target.adminImageRef}`,
106
+ };
107
+ }
108
+ if (target.parallelism !== undefined &&
109
+ (!Number.isInteger(target.parallelism) || target.parallelism < 1 || target.parallelism > 15)) {
110
+ return {
111
+ ok: false,
112
+ done,
113
+ error: `auditParallelism must be a whole number from 1 to 15, got ${target.parallelism}`,
114
+ };
115
+ }
116
+ // 1. The job's own service account.
117
+ const describe = await run([
118
+ "iam",
119
+ "service-accounts",
120
+ "describe",
121
+ sa,
122
+ project,
123
+ "--format=value(email)",
124
+ ]);
125
+ if (!describe.ok) {
126
+ const create = await run([
127
+ "iam",
128
+ "service-accounts",
129
+ "create",
130
+ auditServiceAccountId(env),
131
+ "--display-name=Caelo quality audit",
132
+ "--description=Runs the quality audit's Lighthouse runs in parallel; reads the staged build, writes audit results, nothing else.",
133
+ project,
134
+ ]);
135
+ if (!create.ok && !/already exists/i.test(create.stderr))
136
+ return fail(`create ${sa}`, create);
137
+ }
138
+ done.push(`service account ${sa}`);
139
+ // 2. The results bucket: private, results deleted after a day.
140
+ const owned = await run(["storage", "buckets", "list", project, "--format=value(name)"]);
141
+ if (!owned.ok)
142
+ return fail(`list the buckets of project ${projectId}`, owned);
143
+ const names = owned.stdout.split("\n").map((l) => l
144
+ .trim()
145
+ .replace(/^gs:\/\//, "")
146
+ .replace(/\/$/, ""));
147
+ if (!names.includes(results)) {
148
+ const created = await run([
149
+ "storage",
150
+ "buckets",
151
+ "create",
152
+ resultsUrl,
153
+ project,
154
+ `--location=${region}`,
155
+ "--default-storage-class=STANDARD",
156
+ "--uniform-bucket-level-access",
157
+ "--public-access-prevention",
158
+ "--soft-delete-duration=0",
159
+ ]);
160
+ if (!created.ok) {
161
+ return {
162
+ ok: false,
163
+ done,
164
+ error: /already exists|409/i.test(created.stderr)
165
+ ? `${resultsUrl} exists but does not belong to project ${projectId}; delete or rename that bucket, this install needs the name`
166
+ : `create ${resultsUrl}: ${created.stderr.trim()}`,
167
+ };
168
+ }
169
+ }
170
+ // A private directory (mkdtemp, mode 700): no other local user can swap
171
+ // the file between write and read.
172
+ const dir = mkdtempSync(join(tmpdir(), "caelo-audit-"));
173
+ try {
174
+ const file = join(dir, "lifecycle.json");
175
+ writeFileSync(file, JSON.stringify(AUDIT_RESULTS_LIFECYCLE), { mode: 0o600 });
176
+ const lifecycle = await run([
177
+ "storage",
178
+ "buckets",
179
+ "update",
180
+ resultsUrl,
181
+ project,
182
+ `--lifecycle-file=${file}`,
183
+ ]);
184
+ if (!lifecycle.ok)
185
+ return fail(`set the lifecycle of ${resultsUrl}`, lifecycle);
186
+ }
187
+ finally {
188
+ rmSync(dir, { recursive: true, force: true });
189
+ }
190
+ done.push(`bucket ${resultsUrl} (results deleted after 1 day)`);
191
+ // 3. Bucket grants: the job creates results and (gcp) reads the build;
192
+ // the admin reads results.
193
+ const bucketGrants = [
194
+ { bucket: results, member, role: "roles/storage.objectCreator", why: "tasks write results" },
195
+ ...(provider === "gcp"
196
+ ? ["staging", "static"].map((b) => ({
197
+ bucket: gcpBucketName(projectId, env, b),
198
+ member,
199
+ role: "roles/storage.objectViewer",
200
+ why: "tasks serve the staged build to their browser",
201
+ }))
202
+ : []),
203
+ {
204
+ bucket: results,
205
+ member: runSaMember,
206
+ role: "roles/storage.objectViewer",
207
+ why: "the admin reads the results",
208
+ },
209
+ ];
210
+ for (const g of bucketGrants) {
211
+ const r = await retry([
212
+ "storage",
213
+ "buckets",
214
+ "add-iam-policy-binding",
215
+ `gs://${g.bucket}`,
216
+ `--member=${g.member}`,
217
+ `--role=${g.role}`,
218
+ project,
219
+ "--quiet",
220
+ "--format=none",
221
+ ]);
222
+ if (!r.ok)
223
+ return fail(`grant ${g.role} on gs://${g.bucket} (${g.why})`, r);
224
+ done.push(`${g.member === member ? "audit job" : "admin"}: ${g.role} on gs://${g.bucket}`);
225
+ }
226
+ // 4. Logs.
227
+ const logs = await retry([
228
+ "projects",
229
+ "add-iam-policy-binding",
230
+ projectId,
231
+ `--member=${member}`,
232
+ "--role=roles/logging.logWriter",
233
+ "--condition=None",
234
+ "--quiet",
235
+ "--format=none",
236
+ ]);
237
+ if (!logs.ok)
238
+ return fail("grant the audit job roles/logging.logWriter", logs);
239
+ done.push("audit job: roles/logging.logWriter on the project");
240
+ // 5. The job: the admin image, its own SA, no secrets, no VPC. A job
241
+ // created without a parallelism would run every task at once with no
242
+ // cap; an existing job keeps its own.
243
+ let parallelism = target.parallelism;
244
+ if (parallelism === undefined) {
245
+ const existing = await describeAuditJobImage(run, { projectId, region, env });
246
+ if (!existing.ok)
247
+ return { ok: false, done, error: `read job ${job}: ${existing.error}` };
248
+ if (existing.image === null)
249
+ parallelism = AUDIT_PARALLELISM_DEFAULT;
250
+ }
251
+ const envVars = [
252
+ `CAELO_PROVIDER=${provider}`,
253
+ `CAELO_AUDIT_RESULTS_BUCKET=${results}`,
254
+ ...(provider === "gcp"
255
+ ? [
256
+ `CAELO_STAGING_BUCKET=${gcpBucketName(projectId, env, "staging")}`,
257
+ `CAELO_STATIC_BUCKET=${gcpBucketName(projectId, env, "static")}`,
258
+ ]
259
+ : []),
260
+ ];
261
+ const deploy = await run([
262
+ "run",
263
+ "jobs",
264
+ "deploy",
265
+ job,
266
+ `--image=${target.adminImageRef}`,
267
+ `--region=${region}`,
268
+ project,
269
+ `--service-account=${sa}`,
270
+ "--command=bun",
271
+ `--args=--bun,${AUDIT_TASK_SCRIPT}`,
272
+ `--set-env-vars=${envVars.join(",")}`,
273
+ "--max-retries=0",
274
+ `--task-timeout=${TASK_TIMEOUT}`,
275
+ // Lighthouse wants two cores: the browser and the trace processing.
276
+ "--cpu=2",
277
+ "--memory=2Gi",
278
+ ...(parallelism !== undefined ? [`--parallelism=${parallelism}`] : []),
279
+ "--quiet",
280
+ ]);
281
+ if (!deploy.ok)
282
+ return fail(`deploy job ${job}`, deploy);
283
+ done.push(`job ${job} (${target.adminImageRef.split("@")[1]?.slice(0, 19)}...)`);
284
+ // 6. The admin may run it with its batch and read its executions.
285
+ for (const role of ["roles/run.jobsExecutorWithOverrides", "roles/run.viewer"]) {
286
+ const r = await retry([
287
+ "run",
288
+ "jobs",
289
+ "add-iam-policy-binding",
290
+ job,
291
+ `--region=${region}`,
292
+ project,
293
+ `--member=${runSaMember}`,
294
+ `--role=${role}`,
295
+ "--quiet",
296
+ "--format=none",
297
+ ]);
298
+ if (!r.ok)
299
+ return fail(`grant the admin ${role} on ${job}`, r);
300
+ done.push(`admin: ${role} on ${job}`);
301
+ }
302
+ return { ok: true, done };
303
+ }
304
+ /**
305
+ * {@link ensureAuditJob}'s image: the deployed admin's, so the job runs the
306
+ * code the admin runs.
307
+ */
308
+ export async function resolveAdminImage(opts, deps = {}) {
309
+ const cfg = await readAdminConfig(opts, deps.run ?? defaultGcloud);
310
+ if (!cfg)
311
+ return {
312
+ ok: false,
313
+ error: "could not read the admin service's image (is the admin deployed?)",
314
+ };
315
+ return { ok: true, imageRef: cfg.imageRef };
316
+ }
317
+ //# sourceMappingURL=audit-job-setup.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"audit-job-setup.js","sourceRoot":"","sources":["../src/audit-job-setup.ts"],"names":[],"mappings":"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"}
package/dist/caddy.d.ts CHANGED
@@ -31,5 +31,14 @@ export declare function selfHostedAdminHost(domain: string): string;
31
31
  * public site, and Caddy refuses two site blocks for the same address.
32
32
  */
33
33
  export declare function selfHostedSeedDomains(domain: string, stagingDomain: string): CaddyDomainSpec[];
34
+ /**
35
+ * Why `value` cannot go into the Caddyfile as one token, or null when it can.
36
+ * `init` checks the operator's domains and email with this before it writes
37
+ * anything, so a bad value never leaves a half-made install behind.
38
+ *
39
+ * @param kind `address` for a site address (hostname), `token` for any other value
40
+ */
41
+ export declare function caddyTokenProblem(value: string, kind: "address" | "token"): string | null;
42
+ /** Every caller-supplied value is checked before any of it is emitted (#888). */
34
43
  export declare function generateCaddyfile(spec: CaddyfileSpec): string;
35
44
  //# sourceMappingURL=caddy.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"caddy.d.ts","sourceRoot":"","sources":["../src/caddy.ts"],"names":[],"mappings":"AA2DA,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,IAAI,EAAE,OAAO,GAAG,QAAQ,CAAC;IAClC,QAAQ,CAAC,GAAG,EAAE,YAAY,GAAG,SAAS,CAAC;CACxC;AAED;;;;;GAKG;AACH,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,UAAU,CAAC;AAE/C,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,GAAG,EAAE,YAAY,CAAC;IAC3B,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IACjC,kFAAkF;IAClF,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,0FAA0F;IAC1F,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,OAAO,EAAE,aAAa,CAAC,eAAe,CAAC,CAAC;CAClD;AAED,uFAAuF;AACvF,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAE1D;AAED;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,GAAG,eAAe,EAAE,CAM9F;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,aAAa,GAAG,MAAM,CAqC7D"}
1
+ {"version":3,"file":"caddy.d.ts","sourceRoot":"","sources":["../src/caddy.ts"],"names":[],"mappings":"AA2DA,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,IAAI,EAAE,OAAO,GAAG,QAAQ,CAAC;IAClC,QAAQ,CAAC,GAAG,EAAE,YAAY,GAAG,SAAS,CAAC;CACxC;AAED;;;;;GAKG;AACH,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,UAAU,CAAC;AAE/C,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,GAAG,EAAE,YAAY,CAAC;IAC3B,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IACjC,kFAAkF;IAClF,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,0FAA0F;IAC1F,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,OAAO,EAAE,aAAa,CAAC,eAAe,CAAC,CAAC;CAClD;AAED,uFAAuF;AACvF,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAE1D;AAED;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,GAAG,eAAe,EAAE,CAM9F;AAYD;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,GAAG,OAAO,GAAG,MAAM,GAAG,IAAI,CAOzF;AAWD,iFAAiF;AACjF,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,aAAa,GAAG,MAAM,CA4C7D"}
package/dist/caddy.js CHANGED
@@ -68,9 +68,47 @@ export function selfHostedSeedDomains(domain, stagingDomain) {
68
68
  { hostname: stagingDomain, kind: "public", env: "staging" },
69
69
  ];
70
70
  }
71
+ /**
72
+ * Characters that would let a value end its Caddyfile token and start new
73
+ * directives or blocks: whitespace (incl. newlines), both quote styles Caddy
74
+ * tokenizes (`"` and backtick), braces (blocks and `{placeholders}`), the
75
+ * comment marker, escapes and semicolons. A site address additionally may
76
+ * not hold a comma, which would add further addresses to the same block.
77
+ */
78
+ const UNSAFE_CADDY_TOKEN_RE = /[\s"`{}#\\;]/;
79
+ const UNSAFE_CADDY_ADDRESS_RE = /[\s"`{}#\\;,]/;
80
+ /**
81
+ * Why `value` cannot go into the Caddyfile as one token, or null when it can.
82
+ * `init` checks the operator's domains and email with this before it writes
83
+ * anything, so a bad value never leaves a half-made install behind.
84
+ *
85
+ * @param kind `address` for a site address (hostname), `token` for any other value
86
+ */
87
+ export function caddyTokenProblem(value, kind) {
88
+ if (value.length === 0)
89
+ return "it is empty";
90
+ const unsafe = kind === "address" ? UNSAFE_CADDY_ADDRESS_RE : UNSAFE_CADDY_TOKEN_RE;
91
+ if (unsafe.test(value)) {
92
+ return `it contains whitespace, a quote, a brace, '#', '\\', ';'${kind === "address" ? " or ','" : ""}`;
93
+ }
94
+ return null;
95
+ }
96
+ function assertSafeCaddyToken(label, value, kind) {
97
+ const problem = caddyTokenProblem(value, kind);
98
+ if (problem) {
99
+ throw new Error(`Caddyfile: invalid ${label} ${JSON.stringify(value)} — ${problem}. Fix the value in .caelo/config.json (or the domain you added) and run the command again.`);
100
+ }
101
+ }
102
+ /** Every caller-supplied value is checked before any of it is emitted (#888). */
71
103
  export function generateCaddyfile(spec) {
104
+ assertSafeCaddyToken("ownerEmail", spec.ownerEmail, "token");
105
+ assertSafeCaddyToken("publicSiteRoot", spec.publicSiteRoot, "token");
106
+ assertSafeCaddyToken("stagingSiteRoot", spec.stagingSiteRoot, "token");
107
+ assertSafeCaddyToken("adminUpstream", spec.adminUpstream, "token");
108
+ assertSafeCaddyToken("gatewayUpstream", spec.gatewayUpstream, "token");
72
109
  const hostnames = new Set();
73
110
  for (const d of spec.domains) {
111
+ assertSafeCaddyToken("hostname", d.hostname, "address");
74
112
  if (hostnames.has(d.hostname)) {
75
113
  // Caddy refuses a config with two site blocks for one address, so a
76
114
  // duplicate would take every vhost down, not just this one.
package/dist/caddy.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"caddy.js","sourceRoot":"","sources":["../src/caddy.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"caddy.js","sourceRoot":"","sources":["../src/caddy.ts"],"names":[],"mappings":"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"}
@@ -80,7 +80,7 @@ export const CLI_COMMANDS = [
80
80
  },
81
81
  {
82
82
  name: "rotate-secret",
83
- args: "<name>",
83
+ args: "<name> [--finish [--now]]",
84
84
  summary: "Rotate one of the install's runtime secrets; run without a name to list them.",
85
85
  },
86
86
  {
@@ -1 +1 @@
1
- {"version":3,"file":"cli-routing.js","sourceRoot":"","sources":["../src/cli-routing.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"cli-routing.js","sourceRoot":"","sources":["../src/cli-routing.ts"],"names":[],"mappings":"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"}
package/dist/cli.js CHANGED
@@ -38,10 +38,11 @@ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
38
38
  import { resolve } from "node:path";
39
39
  import { CAELO_VERSION, defaultStagingHost, normalizeStagingHost, stagingHostProblem, } from "@caelo-cms/shared";
40
40
  import { generateBootstrapToken } from "./bootstrap-token.js";
41
- import { generateCaddyfile, selfHostedAdminHost, selfHostedSeedDomains, } from "./caddy.js";
41
+ import { caddyTokenProblem, generateCaddyfile, selfHostedAdminHost, selfHostedSeedDomains, } from "./caddy.js";
42
42
  import { redactCredentials } from "./checkpoint-secrets.js";
43
43
  import { cliUsage, initDelegatesToWizard, resolveCliRoute } from "./cli-routing.js";
44
44
  import { BOOTSTRAP_DIR, COMPOSE_ADMIN_UPSTREAM, COMPOSE_GATEWAY_UPSTREAM, composeProjectName, generateDockerCompose, isPublishablePort, MEDIA_DIR, SITE_OUTPUT_ROOT, selfHostedSiteUrl, } from "./compose.js";
45
+ import { tightenPrivateTree, writePrivateFile } from "./private-file.js";
45
46
  import { selfCommand } from "./self-command.js";
46
47
  import { bunHostRunner, readRelease, recordRelease, releaseImages, resolveVerifiedRelease, } from "./self-hosted-release.js";
47
48
  import { convergeSelfHostedRoles, decideUp, selfHostedBootstrapScript, } from "./self-hosted-roles.js";
@@ -87,8 +88,8 @@ function loadProvider() {
87
88
  }
88
89
  function saveProvider(provider) {
89
90
  if (!existsSync(CAELO_DIR))
90
- mkdirSync(CAELO_DIR, { recursive: true });
91
- writeFileSync(PROVIDER_PATH, JSON.stringify({ provider }, null, 2));
91
+ mkdirSync(CAELO_DIR, { recursive: true, mode: 0o700 });
92
+ writePrivateFile(PROVIDER_PATH, JSON.stringify({ provider }, null, 2));
92
93
  }
93
94
  /**
94
95
  * P15 review pass — guard for commands that only make sense on
@@ -116,6 +117,16 @@ function randomSecret(bytes = 32) {
116
117
  function loadConfig() {
117
118
  if (!existsSync(CONFIG_PATH))
118
119
  return null;
120
+ // Every command that works on an install reads its config first, so this
121
+ // is where an install made by an older CLI loses its world-readable
122
+ // secrets (#888) — not only once each file happens to be rewritten.
123
+ tightenPrivateTree(CAELO_DIR, [BOOTSTRAP_DIR, "backups"], [
124
+ "config.json",
125
+ "provider.json",
126
+ "internal-secret.json",
127
+ "docker-compose.yml",
128
+ `${BOOTSTRAP_DIR}/pending-token.json`,
129
+ ]);
119
130
  const cfg = JSON.parse(readFileSync(CONFIG_PATH, "utf8"));
120
131
  // #707 — settings added later, back-filled (and stored) with what every
121
132
  // install ran before they existed, so `upgrade` / `agent` render the same
@@ -130,8 +141,8 @@ function loadConfig() {
130
141
  }
131
142
  function saveConfig(c) {
132
143
  if (!existsSync(CAELO_DIR))
133
- mkdirSync(CAELO_DIR, { recursive: true });
134
- writeFileSync(CONFIG_PATH, JSON.stringify(c, null, 2));
144
+ mkdirSync(CAELO_DIR, { recursive: true, mode: 0o700 });
145
+ writePrivateFile(CONFIG_PATH, JSON.stringify(c, null, 2));
135
146
  }
136
147
  function arg(name) {
137
148
  const idx = process.argv.indexOf(`--${name}`);
@@ -183,7 +194,7 @@ function emitConfig(cfg, extraDomains = []) {
183
194
  * operator-added domains only `up` / `regenerate-caddy` read back).
184
195
  */
185
196
  function emitComposeFiles(cfg) {
186
- writeFileSync(COMPOSE_PATH, renderCompose(cfg, installImages(cfg)), { mode: 0o600 });
197
+ writePrivateFile(COMPOSE_PATH, renderCompose(cfg, installImages(cfg)));
187
198
  // No secrets (it reads the passwords from the container's environment),
188
199
  // and the postgres user inside the container must be able to read it.
189
200
  writeFileSync(BOOTSTRAP_SCRIPT_PATH, selfHostedBootstrapScript({ agent: activeAgent(cfg) !== undefined }), { mode: 0o755 });
@@ -396,6 +407,19 @@ async function init() {
396
407
  console.error(`--staging-domain: ${stagingProblem}`);
397
408
  process.exit(2);
398
409
  }
410
+ // The Caddyfile check would only fire in emitConfig, after config.json is
411
+ // written — and a re-run then stops at "config already exists".
412
+ for (const [flag, value, kind] of [
413
+ ["--domain", domain, "address"],
414
+ ["--staging-domain", stagingArg, "address"],
415
+ ["--owner-email", ownerEmail, "token"],
416
+ ]) {
417
+ const problem = value === undefined ? null : caddyTokenProblem(value, kind);
418
+ if (problem) {
419
+ console.error(`${flag} ${JSON.stringify(value)}: ${problem}.`);
420
+ process.exit(2);
421
+ }
422
+ }
399
423
  // #696 — the prebuilt release images, resolved to digests and
400
424
  // cosign-verified BEFORE anything is written, so a failed check leaves no
401
425
  // half-made install. `--dev` (contributors only) runs this checkout instead.
@@ -452,7 +476,7 @@ async function init() {
452
476
  // staging file.
453
477
  const tok = generateBootstrapToken();
454
478
  mkdirSync(resolve(CAELO_DIR, BOOTSTRAP_DIR), { recursive: true, mode: 0o700 });
455
- writeFileSync(PENDING_TOKEN_PATH, JSON.stringify(tok, null, 2), { mode: 0o600 });
479
+ writePrivateFile(PENDING_TOKEN_PATH, JSON.stringify(tok, null, 2));
456
480
  // P15.1 — mint the shared HMAC secret for /api/internal/* endpoints.
457
481
  // Same secret is read by:
458
482
  // - admin app (process.env.CAELO_INTERNAL_SECRET),
@@ -463,7 +487,7 @@ async function init() {
463
487
  // internal-secret`.
464
488
  const internalSecret = randomSecret(48);
465
489
  const internalSecretPath = resolve(CAELO_DIR, "internal-secret.json");
466
- writeFileSync(internalSecretPath, JSON.stringify({ secret: internalSecret }, null, 2));
490
+ writePrivateFile(internalSecretPath, JSON.stringify({ secret: internalSecret }, null, 2));
467
491
  // #696 Phase 4c — the update agent (Linux + systemd): the fresh volume's
468
492
  // bootstrap.sh creates its database login on Postgres's first start.
469
493
  const { reportAgentSetup, setUpAgent } = await import("./agent/command.js");
@@ -989,7 +1013,10 @@ async function lifecycleBackup() {
989
1013
  }
990
1014
  async function lifecycleRotateSecret() {
991
1015
  const { rotateSecretCommand } = await import("./lifecycle.js");
992
- await rotateSecretCommand(process.argv[3]);
1016
+ await rotateSecretCommand(process.argv[3], {
1017
+ finish: process.argv.includes("--finish"),
1018
+ now: process.argv.includes("--now"),
1019
+ });
993
1020
  }
994
1021
  async function lifecycleDestroy() {
995
1022
  const { destroyCommand } = await import("./lifecycle.js");