@wardby/cli 0.2.1 → 0.3.0

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 (407) hide show
  1. package/.env.example +34 -4
  2. package/README.md +41 -4
  3. package/dist/claude-coding-worker/driver.d.ts +6 -1
  4. package/dist/claude-coding-worker/driver.js +27 -1
  5. package/dist/claude-coding-worker/main.js +2 -0
  6. package/dist/claude-coding-worker/tool-socket.d.ts +9 -0
  7. package/dist/claude-coding-worker/tool-socket.js +26 -0
  8. package/dist/cli-help.d.ts +1 -1
  9. package/dist/cli-help.js +13 -4
  10. package/dist/cli.d.ts +1 -1
  11. package/dist/cli.js +219 -29
  12. package/dist/coding/collect-exclude.d.ts +25 -0
  13. package/dist/coding/collect-exclude.js +76 -0
  14. package/dist/coding/profile.d.ts +67 -22
  15. package/dist/coding/profile.js +58 -25
  16. package/dist/coding/protected-path-wording.d.ts +24 -0
  17. package/dist/coding/protected-path-wording.js +55 -0
  18. package/dist/coding/protected-paths.d.ts +31 -0
  19. package/dist/coding/protected-paths.js +48 -0
  20. package/dist/coding/protocol.d.ts +75 -4
  21. package/dist/coding/protocol.js +107 -7
  22. package/dist/coding/registry/adapters.d.ts +2 -0
  23. package/dist/coding/registry/adapters.js +9 -0
  24. package/dist/coding/registry/allowlist.d.ts +9 -0
  25. package/dist/coding/registry/allowlist.js +28 -0
  26. package/dist/coding/registry/json-scan.d.ts +55 -0
  27. package/dist/coding/registry/json-scan.js +191 -0
  28. package/dist/coding/registry/lockfiles.d.ts +6 -0
  29. package/dist/coding/registry/lockfiles.js +89 -0
  30. package/dist/coding/registry/npm-lockfile.d.ts +9 -0
  31. package/dist/coding/registry/npm-lockfile.js +73 -0
  32. package/dist/coding/registry/npm-plan.d.ts +29 -0
  33. package/dist/coding/registry/npm-plan.js +289 -0
  34. package/dist/coding/registry/npm.d.ts +7 -0
  35. package/dist/coding/registry/npm.js +252 -0
  36. package/dist/coding/registry/pypi.d.ts +4 -0
  37. package/dist/coding/registry/pypi.js +210 -0
  38. package/dist/coding/registry/report.d.ts +37 -0
  39. package/dist/coding/registry/report.js +31 -0
  40. package/dist/coding/registry/token.d.ts +4 -0
  41. package/dist/coding/registry/token.js +7 -0
  42. package/dist/coding/registry/types.d.ts +286 -0
  43. package/dist/coding/registry/types.js +19 -0
  44. package/dist/coding/registry/worker-config.d.ts +14 -0
  45. package/dist/coding/registry/worker-config.js +31 -0
  46. package/dist/coding/services/builtins.d.ts +22 -0
  47. package/dist/coding/services/builtins.js +85 -0
  48. package/dist/coding/services/catalog.d.ts +443 -0
  49. package/dist/coding/services/catalog.js +159 -0
  50. package/dist/coding/services/declaration.d.ts +11 -0
  51. package/dist/coding/services/declaration.js +114 -0
  52. package/dist/coding/services/note.d.ts +8 -0
  53. package/dist/coding/services/note.js +15 -0
  54. package/dist/coding/services/resolve.d.ts +25 -0
  55. package/dist/coding/services/resolve.js +50 -0
  56. package/dist/coding/services/wording.d.ts +25 -0
  57. package/dist/coding/services/wording.js +70 -0
  58. package/dist/coding-proxy/main.js +24 -4
  59. package/dist/coding-worker/artifact.d.ts +6 -0
  60. package/dist/coding-worker/debug-trace.d.ts +37 -0
  61. package/dist/coding-worker/debug-trace.js +117 -0
  62. package/dist/coding-worker/driver.d.ts +13 -1
  63. package/dist/coding-worker/driver.js +102 -13
  64. package/dist/coding-worker/errors.js +8 -0
  65. package/dist/coding-worker/main.js +10 -2
  66. package/dist/coding-worker/sdk.d.ts +2 -2
  67. package/dist/coding-worker/sdk.js +7 -2
  68. package/dist/coding-worker/types.d.ts +3 -0
  69. package/dist/config/providers.d.ts +41 -0
  70. package/dist/config/providers.js +66 -0
  71. package/dist/core/budget-groups.d.ts +108 -5
  72. package/dist/core/budget-groups.js +125 -19
  73. package/dist/core/budget-wording.d.ts +22 -0
  74. package/dist/core/budget-wording.js +55 -0
  75. package/dist/core/datastores.js +18 -2
  76. package/dist/core/db.d.ts +5 -1
  77. package/dist/core/db.js +8 -2
  78. package/dist/core/dispatch.d.ts +33 -6
  79. package/dist/core/dispatch.js +260 -43
  80. package/dist/core/engine-native.js +27 -10
  81. package/dist/core/grants.d.ts +86 -0
  82. package/dist/core/grants.js +126 -0
  83. package/dist/core/host-events.d.ts +49 -0
  84. package/dist/core/host-events.js +257 -0
  85. package/dist/core/host-identity-links.d.ts +54 -0
  86. package/dist/core/host-identity-links.js +189 -0
  87. package/dist/core/host-status.d.ts +61 -0
  88. package/dist/core/host-status.js +211 -0
  89. package/dist/core/in-flight-runs.d.ts +8 -0
  90. package/dist/core/in-flight-runs.js +56 -0
  91. package/dist/core/provider-wording.d.ts +12 -0
  92. package/dist/core/provider-wording.js +44 -0
  93. package/dist/core/reconciler.d.ts +38 -2
  94. package/dist/core/reconciler.js +86 -2
  95. package/dist/core/repo-access.d.ts +99 -0
  96. package/dist/core/repo-access.js +136 -0
  97. package/dist/core/review-host-checks.d.ts +11 -0
  98. package/dist/core/review-host-checks.js +41 -0
  99. package/dist/core/review-host-tools.d.ts +47 -0
  100. package/dist/core/review-host-tools.js +347 -0
  101. package/dist/core/run-heartbeat.d.ts +27 -0
  102. package/dist/core/run-heartbeat.js +54 -0
  103. package/dist/core/runner.d.ts +30 -8
  104. package/dist/core/runner.js +237 -40
  105. package/dist/core/secrets.d.ts +11 -2
  106. package/dist/core/secrets.js +25 -6
  107. package/dist/core/subagent-memory-tools.d.ts +1 -1
  108. package/dist/core/subagent-memory-tools.js +16 -3
  109. package/dist/core/tool-admin.d.ts +81 -0
  110. package/dist/core/tool-admin.js +129 -0
  111. package/dist/core/tool-names.d.ts +42 -0
  112. package/dist/core/tool-names.js +64 -0
  113. package/dist/core/untrusted-content.d.ts +32 -0
  114. package/dist/core/untrusted-content.js +72 -0
  115. package/dist/core/webhooks.d.ts +8 -1
  116. package/dist/core/webhooks.js +23 -2
  117. package/dist/generated/prisma/browser.d.ts +100 -0
  118. package/dist/generated/prisma/client.d.ts +100 -0
  119. package/dist/generated/prisma/commonInputTypes.d.ts +30 -0
  120. package/dist/generated/prisma/enums.d.ts +6 -0
  121. package/dist/generated/prisma/enums.js +6 -1
  122. package/dist/generated/prisma/internal/class.d.ts +143 -0
  123. package/dist/generated/prisma/internal/class.js +4 -4
  124. package/dist/generated/prisma/internal/prismaNamespace.d.ts +1746 -577
  125. package/dist/generated/prisma/internal/prismaNamespace.js +179 -6
  126. package/dist/generated/prisma/internal/prismaNamespaceBrowser.d.ts +186 -0
  127. package/dist/generated/prisma/internal/prismaNamespaceBrowser.js +179 -6
  128. package/dist/generated/prisma/models/Agent.d.ts +294 -1
  129. package/dist/generated/prisma/models/AgentRepository.d.ts +1425 -0
  130. package/dist/generated/prisma/models/AgentRepository.js +1 -0
  131. package/dist/generated/prisma/models/AgentTool.d.ts +95 -1
  132. package/dist/generated/prisma/models/AuthUser.d.ts +56 -1
  133. package/dist/generated/prisma/models/CodingAgentProfile.d.ts +258 -7
  134. package/dist/generated/prisma/models/CodingProxySession.d.ts +123 -2
  135. package/dist/generated/prisma/models/CodingRun.d.ts +1276 -95
  136. package/dist/generated/prisma/models/CodingService.d.ts +1348 -0
  137. package/dist/generated/prisma/models/CodingService.js +1 -0
  138. package/dist/generated/prisma/models/HostEventDelivery.d.ts +946 -0
  139. package/dist/generated/prisma/models/HostEventDelivery.js +1 -0
  140. package/dist/generated/prisma/models/HostIdentity.d.ts +1232 -0
  141. package/dist/generated/prisma/models/HostIdentity.js +1 -0
  142. package/dist/generated/prisma/models/HostIdentityLinkRequest.d.ts +1473 -0
  143. package/dist/generated/prisma/models/HostIdentityLinkRequest.js +1 -0
  144. package/dist/generated/prisma/models/Principal.d.ts +455 -0
  145. package/dist/generated/prisma/models/RegistryAllowance.d.ts +1148 -0
  146. package/dist/generated/prisma/models/RegistryAllowance.js +1 -0
  147. package/dist/generated/prisma/models/RegistryApprovedVersion.d.ts +1219 -0
  148. package/dist/generated/prisma/models/RegistryApprovedVersion.js +1 -0
  149. package/dist/generated/prisma/models/RegistryFetch.d.ts +1428 -0
  150. package/dist/generated/prisma/models/RegistryFetch.js +1 -0
  151. package/dist/generated/prisma/models/RegistryPlanRefusal.d.ts +1294 -0
  152. package/dist/generated/prisma/models/RegistryPlanRefusal.js +1 -0
  153. package/dist/generated/prisma/models/RegistryVersionFact.d.ts +1085 -0
  154. package/dist/generated/prisma/models/RegistryVersionFact.js +1 -0
  155. package/dist/generated/prisma/models/ResourceGrant.d.ts +1437 -0
  156. package/dist/generated/prisma/models/ResourceGrant.js +1 -0
  157. package/dist/generated/prisma/models/Run.d.ts +389 -1
  158. package/dist/generated/prisma/models/RunHostCheck.d.ts +1239 -0
  159. package/dist/generated/prisma/models/RunHostCheck.js +1 -0
  160. package/dist/generated/prisma/models/RunHostStatus.d.ts +1315 -0
  161. package/dist/generated/prisma/models/RunHostStatus.js +1 -0
  162. package/dist/generated/prisma/models/Tool.d.ts +15 -3
  163. package/dist/generated/prisma/models.d.ts +13 -0
  164. package/dist/help/build.d.ts +1 -0
  165. package/dist/help/build.js +9 -0
  166. package/dist/help/catalog.d.ts +24 -0
  167. package/dist/help/catalog.js +160 -0
  168. package/dist/help/cli.d.ts +2 -0
  169. package/dist/help/cli.js +65 -0
  170. package/dist/help/runtime.d.ts +3 -0
  171. package/dist/help/runtime.js +44 -0
  172. package/dist/help/search.d.ts +9 -0
  173. package/dist/help/search.js +104 -0
  174. package/dist/help-index.json +660 -0
  175. package/dist/import/cli-args.js +3 -2
  176. package/dist/import/create.d.ts +5 -0
  177. package/dist/import/create.js +67 -13
  178. package/dist/import/index.js +19 -9
  179. package/dist/import/neutral-schema.d.ts +11 -11
  180. package/dist/mcp/auth/access.d.ts +70 -0
  181. package/dist/mcp/auth/access.js +58 -0
  182. package/dist/mcp/auth/grants-cli.d.ts +149 -0
  183. package/dist/mcp/auth/grants-cli.js +518 -0
  184. package/dist/mcp/auth/host-account-cli.d.ts +2 -0
  185. package/dist/mcp/auth/host-account-cli.js +47 -0
  186. package/dist/mcp/auth/ownership.d.ts +26 -52
  187. package/dist/mcp/auth/ownership.js +19 -14
  188. package/dist/mcp/auth/repo-authorization.d.ts +22 -0
  189. package/dist/mcp/auth/repo-authorization.js +51 -0
  190. package/dist/mcp/auth/resource-server.d.ts +31 -2
  191. package/dist/mcp/auth/resource-server.js +84 -3
  192. package/dist/mcp/auth/self-hosted/browser.js +2 -2
  193. package/dist/mcp/auth/self-hosted/cli.js +47 -13
  194. package/dist/mcp/auth/self-hosted/credentials.d.ts +20 -2
  195. package/dist/mcp/auth/self-hosted/credentials.js +62 -4
  196. package/dist/mcp/auth/self-hosted/session.d.ts +2 -1
  197. package/dist/mcp/context.d.ts +27 -1
  198. package/dist/mcp/errors.d.ts +25 -6
  199. package/dist/mcp/errors.js +98 -0
  200. package/dist/mcp/host-events/github-ingress.d.ts +36 -0
  201. package/dist/mcp/host-events/github-ingress.js +92 -0
  202. package/dist/mcp/host-events/github-user-callback.d.ts +18 -0
  203. package/dist/mcp/host-events/github-user-callback.js +50 -0
  204. package/dist/mcp/index.d.ts +8 -1
  205. package/dist/mcp/index.js +106 -16
  206. package/dist/mcp/server.js +27 -9
  207. package/dist/mcp/tools/agents.js +446 -48
  208. package/dist/mcp/tools/budget-groups.js +3 -3
  209. package/dist/mcp/tools/datastore.js +22 -15
  210. package/dist/mcp/tools/grants.d.ts +2 -0
  211. package/dist/mcp/tools/grants.js +239 -0
  212. package/dist/mcp/tools/help.d.ts +5 -0
  213. package/dist/mcp/tools/help.js +67 -0
  214. package/dist/mcp/tools/host-accounts.d.ts +2 -0
  215. package/dist/mcp/tools/host-accounts.js +114 -0
  216. package/dist/mcp/tools/memory.d.ts +7 -1
  217. package/dist/mcp/tools/memory.js +5 -5
  218. package/dist/mcp/tools/repositories.d.ts +2 -0
  219. package/dist/mcp/tools/repositories.js +181 -0
  220. package/dist/mcp/tools/runs.d.ts +6 -0
  221. package/dist/mcp/tools/runs.js +60 -6
  222. package/dist/mcp/tools/scheduling.js +3 -6
  223. package/dist/mcp/tools/secrets.js +18 -6
  224. package/dist/mcp/tools/services.d.ts +2 -0
  225. package/dist/mcp/tools/services.js +222 -0
  226. package/dist/mcp/tools/subagents.js +52 -16
  227. package/dist/mcp/tools/tools.d.ts +41 -0
  228. package/dist/mcp/tools/tools.js +201 -40
  229. package/dist/mcp/tools/trigger.js +36 -6
  230. package/dist/mcp/tools/webhooks.js +15 -4
  231. package/dist/mcp/transport/streamable-http.d.ts +10 -0
  232. package/dist/mcp/transport/streamable-http.js +32 -2
  233. package/dist/providers/auth/delegating.d.ts +19 -0
  234. package/dist/providers/auth/delegating.js +71 -0
  235. package/dist/providers/auth/index.d.ts +2 -0
  236. package/dist/providers/auth/index.js +11 -0
  237. package/dist/providers/auth/self-hosted.d.ts +2 -1
  238. package/dist/providers/auth/self-hosted.js +16 -4
  239. package/dist/providers/auth/types.d.ts +6 -0
  240. package/dist/providers/coding-proxy/memory-ledger.d.ts +3 -0
  241. package/dist/providers/coding-proxy/memory-ledger.js +21 -2
  242. package/dist/providers/coding-proxy/metering.d.ts +4 -0
  243. package/dist/providers/coding-proxy/metering.js +28 -2
  244. package/dist/providers/coding-proxy/prisma-ledger.d.ts +17 -0
  245. package/dist/providers/coding-proxy/prisma-ledger.js +50 -5
  246. package/dist/providers/coding-proxy/proxy.d.ts +13 -0
  247. package/dist/providers/coding-proxy/proxy.js +479 -18
  248. package/dist/providers/coding-proxy/registry/audit.d.ts +131 -0
  249. package/dist/providers/coding-proxy/registry/audit.js +380 -0
  250. package/dist/providers/coding-proxy/registry/bounded-fetch.d.ts +16 -0
  251. package/dist/providers/coding-proxy/registry/bounded-fetch.js +77 -0
  252. package/dist/providers/coding-proxy/registry/plan.d.ts +58 -0
  253. package/dist/providers/coding-proxy/registry/plan.js +304 -0
  254. package/dist/providers/coding-proxy/registry/prisma-store.d.ts +34 -0
  255. package/dist/providers/coding-proxy/registry/prisma-store.js +151 -0
  256. package/dist/providers/coding-proxy/registry/service.d.ts +213 -0
  257. package/dist/providers/coding-proxy/registry/service.js +1137 -0
  258. package/dist/providers/coding-proxy/registry/store.d.ts +127 -0
  259. package/dist/providers/coding-proxy/registry/store.js +70 -0
  260. package/dist/providers/coding-proxy/runtime.d.ts +17 -0
  261. package/dist/providers/coding-proxy/runtime.js +63 -0
  262. package/dist/providers/coding-proxy/secure-fetch.js +0 -1
  263. package/dist/providers/coding-proxy/server.d.ts +11 -0
  264. package/dist/providers/coding-proxy/server.js +163 -0
  265. package/dist/providers/coding-proxy/types.d.ts +15 -0
  266. package/dist/providers/engine/types.d.ts +10 -0
  267. package/dist/providers/executor/build.d.ts +2 -2
  268. package/dist/providers/executor/composition.d.ts +3 -0
  269. package/dist/providers/executor/composition.js +19 -0
  270. package/dist/providers/executor/container.d.ts +105 -3
  271. package/dist/providers/executor/container.js +296 -20
  272. package/dist/providers/executor/dbos.d.ts +2 -3
  273. package/dist/providers/executor/in-process.d.ts +2 -3
  274. package/dist/providers/executor/routing.d.ts +7 -0
  275. package/dist/providers/executor/routing.js +13 -0
  276. package/dist/providers/executor/types.d.ts +22 -0
  277. package/dist/providers/jobs/claude-tool-setup.d.ts +23 -0
  278. package/dist/providers/jobs/claude-tool-setup.js +50 -0
  279. package/dist/providers/jobs/collect-prune.d.ts +7 -0
  280. package/dist/providers/jobs/collect-prune.js +27 -0
  281. package/dist/providers/jobs/docker-isolation.d.ts +48 -3
  282. package/dist/providers/jobs/docker-isolation.js +213 -22
  283. package/dist/providers/jobs/docker-services.d.ts +35 -0
  284. package/dist/providers/jobs/docker-services.js +191 -0
  285. package/dist/providers/jobs/docker.d.ts +49 -2
  286. package/dist/providers/jobs/docker.js +279 -32
  287. package/dist/providers/jobs/fake-kubernetes-api.d.ts +1 -0
  288. package/dist/providers/jobs/fake-kubernetes-api.js +12 -3
  289. package/dist/providers/jobs/kubernetes-isolation.d.ts +17 -2
  290. package/dist/providers/jobs/kubernetes-isolation.js +238 -57
  291. package/dist/providers/jobs/kubernetes-platform.d.ts +10 -4
  292. package/dist/providers/jobs/kubernetes-platform.js +11 -5
  293. package/dist/providers/jobs/kubernetes-preflight.js +3 -0
  294. package/dist/providers/jobs/kubernetes.d.ts +21 -2
  295. package/dist/providers/jobs/kubernetes.js +112 -21
  296. package/dist/providers/jobs/types.d.ts +18 -0
  297. package/dist/providers/llm/anthropic.js +6 -2
  298. package/dist/providers/llm/bedrock.js +2 -1
  299. package/dist/providers/llm/claude-messages.d.ts +5 -1
  300. package/dist/providers/llm/claude-messages.js +1 -0
  301. package/dist/providers/llm/claude-provider.d.ts +3 -1
  302. package/dist/providers/llm/claude-provider.js +5 -1
  303. package/dist/providers/llm/index.d.ts +1 -1
  304. package/dist/providers/llm/index.js +1 -1
  305. package/dist/providers/llm/pricing-anthropic.d.ts +10 -0
  306. package/dist/providers/llm/pricing-anthropic.js +32 -14
  307. package/dist/providers/llm/pricing-bedrock-claude.d.ts +8 -0
  308. package/dist/providers/llm/pricing-bedrock-claude.js +9 -0
  309. package/dist/providers/llm/routing.d.ts +10 -1
  310. package/dist/providers/llm/routing.js +18 -0
  311. package/dist/providers/llm/types.d.ts +12 -0
  312. package/dist/providers/llm/types.js +8 -1
  313. package/dist/providers/review-host/diff-lines.d.ts +16 -0
  314. package/dist/providers/review-host/diff-lines.js +59 -0
  315. package/dist/providers/review-host/github-events.d.ts +6 -0
  316. package/dist/providers/review-host/github-events.js +180 -0
  317. package/dist/providers/review-host/github-user-auth.d.ts +38 -0
  318. package/dist/providers/review-host/github-user-auth.js +128 -0
  319. package/dist/providers/review-host/github.d.ts +57 -0
  320. package/dist/providers/review-host/github.js +567 -0
  321. package/dist/providers/review-host/index.d.ts +12 -0
  322. package/dist/providers/review-host/index.js +30 -0
  323. package/dist/providers/review-host/review-format.d.ts +19 -0
  324. package/dist/providers/review-host/review-format.js +59 -0
  325. package/dist/providers/review-host/types.d.ts +284 -0
  326. package/dist/providers/review-host/types.js +24 -0
  327. package/dist/providers/vcs/git.d.ts +16 -6
  328. package/dist/providers/vcs/git.js +77 -13
  329. package/dist/providers/vcs/github.d.ts +74 -3
  330. package/dist/providers/vcs/github.js +195 -15
  331. package/dist/providers/vcs/types.d.ts +42 -4
  332. package/dist/quickstart/index.js +13 -2
  333. package/dist/sandbox/fetch-policy.d.ts +20 -2
  334. package/dist/sandbox/fetch-policy.js +64 -3
  335. package/dist/sandbox/host-functions.d.ts +9 -1
  336. package/dist/sandbox/host-functions.js +12 -5
  337. package/dist/serve.js +1 -1
  338. package/dist/wardby-bin.js +6 -0
  339. package/docs/README.md +30 -0
  340. package/docs/architecture-runtime.md +90 -0
  341. package/docs/assets/brand/wardby-icon-512.png +0 -0
  342. package/docs/assets/brand/wardby-icon.svg +16 -0
  343. package/docs/assets/brand/wardby-mascot-profile-512.png +0 -0
  344. package/docs/assets/brand/wardby-mascot.png +0 -0
  345. package/docs/assets/brand/wardby-mascot.svg +5 -0
  346. package/docs/assets/wardby-workflow.svg +106 -0
  347. package/docs/code-review-agents.md +481 -0
  348. package/docs/coding-agent-setup.md +172 -0
  349. package/docs/coding-packages.md +455 -0
  350. package/docs/coding-services.md +300 -0
  351. package/docs/coding-worker-byo-images.md +98 -0
  352. package/docs/coding-worker-isolation.md +1061 -0
  353. package/docs/getting-started-gke.md +615 -0
  354. package/docs/getting-started-identity-provider.md +308 -0
  355. package/docs/getting-started.md +133 -0
  356. package/docs/observability.md +53 -0
  357. package/docs/release-verification.md +66 -0
  358. package/docs/security-deployment.md +657 -0
  359. package/help/code-review-agents.md +31 -0
  360. package/help/coding-packages.md +31 -0
  361. package/help/coding-services.md +71 -0
  362. package/help/creating-agents.md +68 -0
  363. package/help/deploy-gke.md +39 -0
  364. package/help/deployment-targets.md +39 -0
  365. package/help/errors/budget-group-exhausted.md +25 -0
  366. package/help/errors/docker-isolation-unsupported.md +26 -0
  367. package/help/errors/protected-path.md +45 -0
  368. package/help/errors/repo-access.md +27 -0
  369. package/help/errors/service-declaration-invalid.md +27 -0
  370. package/help/errors/service-declaration-unavailable.md +25 -0
  371. package/help/errors/service-launcher-unsupported.md +30 -0
  372. package/help/errors/service-not-allowed.md +27 -0
  373. package/help/errors/service-unknown.md +23 -0
  374. package/help/errors/service-unready.md +49 -0
  375. package/help/getting-started.md +31 -0
  376. package/help/github.md +30 -0
  377. package/help/identity-and-access.md +39 -0
  378. package/help/mcp.md +30 -0
  379. package/help/native-capabilities.md +30 -0
  380. package/help/observability.md +41 -0
  381. package/help/operating-agents.md +30 -0
  382. package/help/security.md +27 -0
  383. package/help/troubleshooting/budgets.md +26 -0
  384. package/help/troubleshooting/coding-workers.md +47 -0
  385. package/help/troubleshooting/repository-access.md +27 -0
  386. package/package.json +12 -3
  387. package/prisma/migrations/20260925010000_coding_collect_exclude/migration.sql +8 -0
  388. package/prisma/migrations/20260925015000_allowed_egress_default/migration.sql +8 -0
  389. package/prisma/migrations/20260925020000_coding_package_registry/migration.sql +55 -0
  390. package/prisma/migrations/20260925030000_tool_name_per_owner/migration.sql +11 -0
  391. package/prisma/migrations/20260926010000_run_trigger_host_event/migration.sql +7 -0
  392. package/prisma/migrations/20260926020000_code_review_hosts/migration.sql +53 -0
  393. package/prisma/migrations/20260926030000_auth_user_roles/migration.sql +9 -0
  394. package/prisma/migrations/20260926040000_agent_effort/migration.sql +6 -0
  395. package/prisma/migrations/20260926050000_registry_lockfile_plan/migration.sql +32 -0
  396. package/prisma/migrations/20260926050000_repo_access_authorization/migration.sql +82 -0
  397. package/prisma/migrations/20260926060000_registry_plan_refusal/migration.sql +19 -0
  398. package/prisma/migrations/20260926100000_registry_plan_refusal_published_at/migration.sql +5 -0
  399. package/prisma/migrations/20260926190000_run_host_status/migration.sql +18 -0
  400. package/prisma/migrations/20260926210000_run_host_status_at_dispatch/migration.sql +5 -0
  401. package/prisma/migrations/20260927010000_resource_grants/migration.sql +72 -0
  402. package/prisma/migrations/20260927020000_proxy_session_budget_exhausted/migration.sql +3 -0
  403. package/prisma/migrations/20260927030000_coding_debug_trace/migration.sql +4 -0
  404. package/prisma/migrations/20260927040000_proxy_session_upstream_failure/migration.sql +3 -0
  405. package/prisma/migrations/20260927050000_coding_run_services/migration.sql +74 -0
  406. package/prisma/migrations/20260928000000_coding_run_tool_image/migration.sql +5 -0
  407. package/prisma/schema.prisma +407 -21
@@ -0,0 +1,615 @@
1
+ # Getting started on GKE
2
+
3
+ This is the supported Google Cloud deployment path for Wardby. It creates a
4
+ GKE Autopilot cluster, Artifact Registry, private-IP Cloud SQL for PostgreSQL,
5
+ isolated coding workers, an HTTPS Gateway, and the Wardby control plane.
6
+
7
+ The older `deploy/gcp` Cloud Run module is deprecated. New deployments should
8
+ use `deploy/gke` and the `gke-autopilot` Kubernetes overlay described here.
9
+
10
+ > This deployment creates billable Google Cloud resources. Use a dedicated
11
+ > project, review `terraform plan`, configure budgets and alerts, and understand
12
+ > the teardown procedure before applying it.
13
+
14
+ ## Architecture
15
+
16
+ - The Wardby control plane and coding proxy run in GKE Autopilot.
17
+ - Each Codex coding run uses an ephemeral gVisor-backed pod.
18
+ - Cloud SQL PostgreSQL has no public IP and is reached through private services
19
+ access on the cluster's VPC.
20
+ - Runtime, migration, and worker images are stored in Artifact Registry and
21
+ deployed by immutable digest. Two worker images are published: the default
22
+ `node` toolchain and `node-python` (Debian's Python 3 with pytest and ruff).
23
+ Coding agents select the latter with `toolchain: "node-python"` and
24
+ `toolchainVersion: "3.12"`, the key it is registered under.
25
+ - A global GKE Gateway terminates TLS with Certificate Manager and applies a
26
+ Cloud Armor rate-limit policy.
27
+ - Namespace RBAC and default-deny network policies constrain the launcher,
28
+ proxy, control plane, and worker pods.
29
+ - Native runs use the durable executor (`EXECUTOR=dbos`), so a run survives
30
+ the control-plane pod being preempted or rescheduled. See "Durable executor"
31
+ below.
32
+
33
+ The Kubernetes launcher runs both Codex and Claude Code coding workers; see
34
+ [Coding worker isolation](coding-worker-isolation.md#pod-layout) for the
35
+ Claude Code pod's extra tool-runner sidecar and its image requirements.
36
+
37
+ ## 1. Prerequisites
38
+
39
+ Install and authenticate:
40
+
41
+ - Google Cloud CLI (`gcloud`)
42
+ - Terraform 1.10 or newer
43
+ - Docker with `linux/amd64` build support
44
+ - `kubectl`
45
+ - Helm 3 or later (installs External Secrets Operator)
46
+ - Node.js 24 and npm
47
+
48
+ You also need:
49
+
50
+ - a Google Cloud project with billing enabled;
51
+ - a DNS hostname you control, such as `wardby.example.com`;
52
+ - an OpenAI API key and an Anthropic API key (the current deployment script
53
+ provisions both proxy routes);
54
+ - a dedicated GitHub App installed on the repositories coding agents may use;
55
+ and
56
+ - permission to create GKE, Cloud SQL, networking, Artifact Registry,
57
+ Certificate Manager, and Cloud Armor resources.
58
+
59
+ Clone the repository because the cloud deployment assets are not exposed as an
60
+ `npx` deployment command:
61
+
62
+ ```sh
63
+ git clone https://github.com/wardby/wardby.git
64
+ cd wardby
65
+ npm ci
66
+ ```
67
+
68
+ Authenticate both Terraform and Docker:
69
+
70
+ ```sh
71
+ gcloud auth login
72
+ gcloud auth application-default login
73
+ gcloud config set project YOUR_PROJECT_ID
74
+ gcloud auth configure-docker YOUR_REGION-docker.pkg.dev
75
+ ```
76
+
77
+ ## 2. Enable Google Cloud APIs
78
+
79
+ ```sh
80
+ gcloud services enable \
81
+ artifactregistry.googleapis.com \
82
+ certificatemanager.googleapis.com \
83
+ cloudresourcemanager.googleapis.com \
84
+ compute.googleapis.com \
85
+ container.googleapis.com \
86
+ iam.googleapis.com \
87
+ networksecurity.googleapis.com \
88
+ secretmanager.googleapis.com \
89
+ servicenetworking.googleapis.com \
90
+ sqladmin.googleapis.com
91
+ ```
92
+
93
+ API enablement can take several minutes to propagate.
94
+
95
+ ## 3. Configure Terraform
96
+
97
+ ```sh
98
+ cp deploy/gke/terraform.tfvars.example deploy/gke/terraform.tfvars
99
+ ```
100
+
101
+ Set `project_id`, `region`, and the VPC/subnetwork. The selected network **must
102
+ be the network used by the cluster** because private-services peering is not
103
+ transitive.
104
+
105
+ Use a remote Terraform backend for shared or production deployments: state
106
+ tracks real infrastructure, and losing it is expensive to reconstruct even
107
+ though it holds no secret. Every workload logs in through Cloud SQL IAM, so
108
+ state contains no database password; Terraform creates the Secret Manager
109
+ secrets empty too.
110
+
111
+ Review before applying:
112
+
113
+ ```sh
114
+ terraform -chdir=deploy/gke init
115
+ terraform -chdir=deploy/gke validate
116
+ terraform -chdir=deploy/gke plan
117
+ terraform -chdir=deploy/gke apply
118
+ ```
119
+
120
+ Terraform creates the Autopilot cluster with the standard Gateway API channel,
121
+ Artifact Registry, private service range, Cloud SQL instance, database, and
122
+ database user.
123
+
124
+ Fetch the cluster context and verify the Gateway classes:
125
+
126
+ ```sh
127
+ $(terraform -chdir=deploy/gke output -raw kubectl_context_command)
128
+ kubectl get gatewayclass
129
+ ```
130
+
131
+ `gke-l7-global-external-managed` must report `ACCEPTED=True` before continuing.
132
+ Google notes that Gateway API enablement can take significant time to reconcile.
133
+
134
+ ## 4. Prepare secrets
135
+
136
+ Create `.env.local` at the repository root and keep it untracked:
137
+
138
+ ```dotenv
139
+ OPENAI_API_KEY="..."
140
+ ANTHROPIC_API_KEY="..."
141
+ SECRET_APP_KEY="64_HEX_CHARACTERS"
142
+ GITHUB_APP_ID="..."
143
+ GITHUB_APP_PRIVATE_KEY="-----BEGIN PRIVATE KEY-----
144
+ ...
145
+ -----END PRIVATE KEY-----"
146
+ GITHUB_APP_CLIENT_ID="..."
147
+ GITHUB_APP_CLIENT_SECRET="..."
148
+ ```
149
+
150
+ `GITHUB_APP_CLIENT_ID` and `GITHUB_APP_CLIENT_SECRET` are the App's OAuth
151
+ Client ID (not the App ID) and a client secret generated under the App's
152
+ **Client secrets**. Users link their GitHub accounts with them, which is how
153
+ wardby checks that an agent's owner may use a repository. Also set the App's
154
+ **Callback URL** to `https://<your hostname>/hosts/github/user-callback` and
155
+ leave **Request user authorization (OAuth) during installation** unchecked; see
156
+ [code-review-agents.md](code-review-agents.md#registering-the-github-app).
157
+ Seeding stops, with nothing written, if either value is missing.
158
+
159
+ Generate `SECRET_APP_KEY` with:
160
+
161
+ ```sh
162
+ openssl rand -hex 32
163
+ ```
164
+
165
+ `.env.local` is only the **first-time source**. On its first run `up.sh`
166
+ copies each value into Google Secret Manager, over stdin and never in a process
167
+ argument, and External Secrets Operator syncs them into the cluster from then
168
+ on. A value already in Secret Manager is never overwritten, so after the first
169
+ deployment Secret Manager is the source of truth. You can then remove the
170
+ production values from `.env.local`.
171
+
172
+ Redeploying onto a cluster that already runs Wardby carries its existing
173
+ `SECRET_APP_KEY` and login signing keys over, so stored credentials still
174
+ decrypt and nobody has to sign in again.
175
+
176
+ ## 5. Prepare the public edge
177
+
178
+ The committed Gateway expects three global resources named
179
+ `wardby-control-plane`: a static address, Certificate Manager certificate map,
180
+ and Cloud Armor security policy.
181
+
182
+ Reserve the address and create the policy:
183
+
184
+ ```sh
185
+ gcloud compute addresses create wardby-control-plane --global
186
+ gcloud compute security-policies create wardby-control-plane
187
+ gcloud compute security-policies rules create 1000 \
188
+ --security-policy=wardby-control-plane \
189
+ --src-ip-ranges='*' \
190
+ --action=throttle \
191
+ --rate-limit-threshold-count=300 \
192
+ --rate-limit-threshold-interval-sec=60 \
193
+ --conform-action=allow \
194
+ --exceed-action=deny-429 \
195
+ --enforce-on-key=IP
196
+ ```
197
+
198
+ Create a DNS authorization for your hostname:
199
+
200
+ ```sh
201
+ gcloud certificate-manager dns-authorizations create wardby-control-plane \
202
+ --domain=wardby.example.com
203
+ gcloud certificate-manager dns-authorizations describe wardby-control-plane
204
+ ```
205
+
206
+ Add the returned `dnsResourceRecord` CNAME to your DNS provider. Then create
207
+ the certificate and map:
208
+
209
+ ```sh
210
+ gcloud certificate-manager certificates create wardby-control-plane \
211
+ --domains=wardby.example.com \
212
+ --dns-authorizations=wardby-control-plane
213
+ gcloud certificate-manager maps create wardby-control-plane
214
+ gcloud certificate-manager maps entries create wardby-control-plane \
215
+ --map=wardby-control-plane \
216
+ --certificates=wardby-control-plane \
217
+ --hostname=wardby.example.com
218
+ ```
219
+
220
+ Wait for the certificate and map entry to become active. These steps follow
221
+ Google's [Certificate Manager DNS-authorization
222
+ procedure](https://cloud.google.com/certificate-manager/docs/deploy-google-managed-dns-auth)
223
+ and [GKE Gateway guidance](https://cloud.google.com/kubernetes-engine/docs/how-to/deploying-gateways).
224
+
225
+ ## 6. Deploy Wardby
226
+
227
+ The deployment script is idempotent. It converges Terraform, builds and pushes
228
+ `linux/amd64` images, resolves immutable digests, seeds Secret Manager and installs External Secrets Operator to sync it,
229
+ renders the GKE overlay, and waits for the proxy and control plane:
230
+
231
+ ```sh
232
+ HOSTNAME=wardby.example.com deploy/gke/up.sh
233
+ ```
234
+
235
+ Point the hostname's public A record at the reserved address:
236
+
237
+ ```sh
238
+ gcloud compute addresses describe wardby-control-plane \
239
+ --global --format='value(address)'
240
+ ```
241
+
242
+ Allow DNS and the managed certificate to converge before treating an HTTPS
243
+ failure as an application failure.
244
+
245
+ On a brand-new project, run `terraform apply` and then
246
+ `bootstrap-database-iam.sh` before `up.sh`: the bootstrap has to grant the
247
+ migrator before any migration can run. See "Database login" below for the
248
+ exact order, including the database check `up.sh` doesn't pass until a second
249
+ bootstrap run has granted the coding proxy too.
250
+
251
+ ### Database login
252
+
253
+ Each workload authenticates to Cloud SQL as its own Google service account,
254
+ through the Cloud SQL Auth Proxy, via Workload Identity from one Kubernetes
255
+ service account. What each may do inside the database comes from a `NOLOGIN`
256
+ group role in `deploy/gke/database-grants.sql`:
257
+
258
+ | Workload | Google service account | Kubernetes service account | May do |
259
+ | ------------- | ------------------------ | -------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
260
+ | Control plane | `<name_prefix>-app` | `wardby-control-plane` | `wardby_app`: read/write every table, including the durable executor's in schema `dbos`; never change a schema |
261
+ | Coding proxy | `<name_prefix>-proxy` | `wardby-coding-proxy` | `wardby_proxy`: only its budget ledger — `CodingProxySession`/`CodingProxyRequest`, plus update `tokensIn`, `tokensOut` and `costUsd` on `Run`, and read only its `id` |
262
+ | Migrations | `<name_prefix>-migrator` | `wardby-migrator` | Acts as the table owner (`SET ROLE`), so `prisma migrate deploy` and `dbos schema` can alter and create tables |
263
+
264
+ `deploy/gke/bootstrap-database-iam.sh` applies the grants as the built-in
265
+ owner, from a short-lived Job inside the cluster. Run it whenever
266
+ `database-grants.sql` changes, and as part of the orders below. The grants run
267
+ in one transaction, so a statement the database refuses leaves nothing applied.
268
+
269
+ The built-in owner is not a Terraform resource: `bootstrap-database-iam.sh`
270
+ creates it if it's missing, sets a one-time password on it through the Cloud
271
+ SQL Admin API, uses that password once to apply the grants, and resets it to
272
+ a value nobody holds on every exit, including a failed grant. No password is
273
+ ever stored, printed, or passed as a process argument.
274
+
275
+ A brand-new project runs, in this order:
276
+
277
+ 1. `terraform -chdir=deploy/gke apply` — creates the cluster, the instance
278
+ and the three IAM database users. Run it yourself rather than through
279
+ `up.sh`, which would go on to the migrations before the migrator has its
280
+ grants.
281
+ 2. `deploy/gke/bootstrap-database-iam.sh` (default mode) — fetches the
282
+ cluster's kubectl credentials if you don't have them yet, creates the
283
+ owner, and applies the migrator's (and the app's) grants. The coding
284
+ proxy's ledger tables don't exist yet, so its grants are skipped: expected.
285
+ 3. `deploy/gke/up.sh` — applies the migrations and rolls out, then stops at
286
+ the database check: the coding proxy's grants are on tables that did not
287
+ exist in step 2.
288
+ 4. `deploy/gke/bootstrap-database-iam.sh` again — now that the tables exist,
289
+ also applies the coding proxy's ledger grants.
290
+ 5. `deploy/gke/up.sh` — rolls out again and passes every check.
291
+
292
+ Two bootstrap runs, not one: a grant on a named table can't apply before the
293
+ migration that creates that table has run.
294
+
295
+ The same applies to every later release that adds a table the coding proxy
296
+ uses: run `bootstrap-database-iam.sh` again after its migration. `up.sh`
297
+ checks this for you. After the rollout it asks the database, as the proxy's
298
+ own role, for every privilege `database-grants.sql` gives `wardby_proxy`
299
+ (`deploy/gke/proxy-grant-checks.mjs`), and stops with the list of missing
300
+ grants instead of letting the proxy fail registry requests with "permission
301
+ denied".
302
+
303
+ `bootstrap-database-iam.sh --check` runs the grants inside a transaction that
304
+ is then rolled back, and reports success or the exact statement the database
305
+ refused, without changing anything. Run it before the real run on a live
306
+ deployment.
307
+
308
+ `connector_enforcement` (the Cloud SQL instance setting) defaults to
309
+ `REQUIRED`: it refuses any connection that does not come through the Auth
310
+ Proxy or a Cloud SQL connector, and with port 5432 to the instance closed in
311
+ every NetworkPolicy, there's no route left for a plain Postgres connection to
312
+ even attempt. Leave it at the default; an older deployment still on password
313
+ login moves off it on an earlier revision of this module, below.
314
+
315
+ ### Moving an older deployment
316
+
317
+ A deployment still on password login — from before this module retired it —
318
+ can't adopt this revision directly: this revision no longer syncs the
319
+ password `DATABASE_URL`, closes port 5432 and refuses non-Auth-Proxy
320
+ connections, which would cut off pods still logging in with the password.
321
+ It moves over in stages, because real pods are already serving traffic
322
+ throughout.
323
+
324
+ **1–2. Cut the running pods to IAM login, on the revision before this one.**
325
+ Check out the revision of this module that added IAM login, before the
326
+ password was retired: the merge of pull request #83 into `main`.
327
+ `git log --oneline --first-parent main -- deploy/gke` lists the merges that
328
+ changed this module, newest first; it's the one titled
329
+ `Merge pull request #83 ...`, just below the merge that retired the password
330
+ (`git log --oneline --merges --grep='#83' main` finds it directly). Then
331
+ `git checkout <that commit>` and follow that revision's own
332
+ `docs/getting-started-gke.md`, "Database login", and its order for "an
333
+ existing deployment still on password login": `bootstrap-database-iam.sh
334
+ --password-from-stdin --check`, the same without `--check`, then `up.sh`.
335
+ That rolls out password-less pods while the password still works as a
336
+ fallback, and proves the control plane and the coding proxy each read the
337
+ database through their own IAM login. That revision's
338
+ `connector_enforcement` default is still `NOT_REQUIRED`, so nothing that
339
+ still uses the password is cut off. Then return to this revision
340
+ (`git checkout main`) for the last stage.
341
+
342
+ **3. Retire the password**, once every pod speaks IAM login and this change
343
+ has merged to `main`:
344
+
345
+ 1. Confirm nothing still uses the password: both Deployments' pods log in as
346
+ IAM users, and no pod has a password `DATABASE_URL` in its effective env.
347
+ 2. Delete the `database-url` Secret Manager secret:
348
+ `gcloud secrets delete <name_prefix>-database-url --quiet` — this module no
349
+ longer creates or reads it. Deleting it by hand first, before the next
350
+ step, is what lets Terraform drop it from state instead of trying to
351
+ destroy it.
352
+ 3. Remove any `connector_enforcement` line from `terraform.tfvars` (or set
353
+ it to `REQUIRED`), so the new default takes effect. Then `terraform plan`:
354
+ expect the generated password destroyed, the `database-url` secret and
355
+ its IAM binding gone from state (already deleted by hand), the old
356
+ password-login user forgotten (not destroyed), `connector_enforcement`
357
+ moving to `REQUIRED` on the instance (updated in place, not replaced),
358
+ and no other destroy. Review
359
+ the plan, then apply only once it matches that. If apply still fails
360
+ trying to destroy the `database-url` secret, the secret wasn't actually
361
+ deleted in step 2 — delete it and re-apply. Never turn off
362
+ `secrets_deletion_protection` to get past that error: it unprotects every
363
+ other secret in the same set, including `SECRET_APP_KEY` and the auth
364
+ keys, and losing `SECRET_APP_KEY` makes every credential already stored
365
+ in the database unreadable.
366
+ 4. `deploy/gke/up.sh` — new ExternalSecrets carry no `DATABASE_URL`,
367
+ NetworkPolicies no longer allow 5432, migrations, rollout, and the
368
+ database and endpoint checks.
369
+ 5. `deploy/gke/bootstrap-database-iam.sh` (default mode) — the guard now
370
+ passes, since the Secret no longer has a `DATABASE_URL`: it sets a
371
+ one-time owner password, reapplies the grants, and resets the password to
372
+ a value nobody holds.
373
+ 6. Verify: a direct password connection is refused (NetworkPolicy blocks
374
+ 5432, and the instance refuses it too), the pods still log in through
375
+ IAM, and the Terraform state holds no database password.
376
+
377
+ ## 7. Verify
378
+
379
+ ```sh
380
+ kubectl -n wardby-coding get pods
381
+ kubectl -n wardby-coding get gateway,httproute
382
+ kubectl -n wardby-coding describe gateway wardby-control-plane
383
+
384
+ curl -sS -o /dev/null -w '%{http_code}\n' \
385
+ https://wardby.example.com/.well-known/oauth-protected-resource
386
+ curl -sS -o /dev/null -w '%{http_code}\n' -X POST \
387
+ -H 'content-type: application/json' \
388
+ -H 'accept: application/json, text/event-stream' \
389
+ -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{}}' \
390
+ https://wardby.example.com/mcp
391
+ ```
392
+
393
+ The discovery request should return `200`; an unauthenticated MCP
394
+ `initialize` request should return `401`. Send the JSON body: without it the
395
+ server answers `415`, because it checks the content type before the token.
396
+
397
+ Create the first self-hosted login credential. It is printed once.
398
+ `--role admin` makes this operator account an admin. Only admins can use all
399
+ the privileged operations: `make_owner`, BYO `workerImageRef`, package
400
+ approval, and service catalog changes. A `package-approver` can approve
401
+ packages only, and a `service-manager` can change the service catalog only.
402
+ Users created without `--role` have no roles. See
403
+ [roles and privileged operations](security-deployment.md#roles-and-privileged-operations).
404
+
405
+ ```sh
406
+ kubectl exec -n wardby-coding deploy/wardby-control-plane \
407
+ -c control-plane -- \
408
+ node dist/cli.js auth user create --subject YOUR_SUBJECT --role admin
409
+ ```
410
+
411
+ When you upgrade a deployment created before roles existed, every existing
412
+ user has no roles, including you. Grant the admin role to yourself, then
413
+ reconnect your MCP client:
414
+
415
+ ```sh
416
+ kubectl exec -n wardby-coding deploy/wardby-control-plane \
417
+ -c control-plane -- \
418
+ node dist/cli.js auth user grant --subject YOUR_SUBJECT --role admin
419
+ ```
420
+
421
+ The deployment helper configures Wardby's self-hosted authorization server by
422
+ default. To use your organization's OAuth/OIDC provider instead, configure the
423
+ control-plane secret for delegated authentication and follow
424
+ [Bring your own identity provider](getting-started-identity-provider.md). In
425
+ delegated mode, users and clients belong to the external provider and you do
426
+ not create Wardby login credentials.
427
+
428
+ Run the coding boundary preflight inside the configured control-plane pod:
429
+
430
+ ```sh
431
+ kubectl exec -n wardby-coding deploy/wardby-control-plane \
432
+ -c control-plane -- \
433
+ node dist/cli.js coding preflight
434
+ ```
435
+
436
+ Before production use, complete the [release verification](release-verification.md)
437
+ and [security deployment](security-deployment.md) checklists.
438
+
439
+ ## 8. Operate and update
440
+
441
+ Re-run `deploy/gke/up.sh` after a source or configuration change. It rebuilds
442
+ images, pushes them, substitutes immutable digests, leaves Secret Manager values
443
+ as they are, waits for rollouts, and then checks the public endpoint: discovery
444
+ must answer `200` and an unauthenticated MCP request `401`, or the deploy fails.
445
+
446
+ A control-plane restart is designed not to drop requests, at the cost of a
447
+ slower rollout. A new pod must stay Ready for three minutes before the old one
448
+ is retired, because the load balancer can take well over a minute to start
449
+ routing to a new pod even after its health check passes; the old pod then keeps
450
+ serving for 30 seconds while the load balancer drains it. `up.sh` checks the
451
+ endpoint only after the old pod is gone, and requires a steady minute of
452
+ successful responses.
453
+
454
+ Migrations run as a `wardby-migrate-<unix time>` Job before the Deployments
455
+ roll; if it fails, `up.sh` prints the `migrate` and `cloud-sql-proxy` container
456
+ logs (and, if those are empty or the Job timed out, the Job's and pod's
457
+ events), then stops.
458
+
459
+ ### Durable executor
460
+
461
+ The control plane runs native runs on the durable executor (`EXECUTOR=dbos`):
462
+ each LLM turn and tool call is checkpointed in Postgres, in schema `dbos`. If
463
+ the control-plane pod is preempted, evicted or rescheduled, its replacement
464
+ picks each interrupted run up from its last completed step once the run's
465
+ heartbeat times out (about a minute after the replacement is running). Only
466
+ the step that was in flight runs again. Coding runs are Kubernetes Jobs and are unaffected.
467
+
468
+ - **Schema.** The migration Job creates and migrates `dbos` as the migrator
469
+ (`npm run dbos:migrate`) after the Prisma migrations, so it always runs
470
+ before the control plane starts. The control plane's role only reads and
471
+ writes it: `database-grants.sql` grants `wardby_app` `USAGE` on the schema,
472
+ `SELECT`/`INSERT`/`UPDATE`/`DELETE` on its tables, and the same through the
473
+ owner's default privileges for tables a later DBOS version adds.
474
+ - **Executor id.** `DBOS_EXECUTOR_ID` is left unset, so every process gets a
475
+ random one. A replacement pod does not need the old pod's id: the
476
+ reconciler adopts any interrupted run, whichever process started it. This is
477
+ also what makes the overlapping rolling update safe.
478
+ - **Version.** `up.sh` sets `DBOS__APPVERSION` to the runtime image digest. A
479
+ run resumes only under the version that started it: across a pod move, or an
480
+ `up.sh` re-run whose source did not change, runs continue. When you deploy
481
+ new code, the old pod first **drains**: after its 30-second `preStop` it
482
+ stops the scheduler and waits for the runs it is executing to finish, up to
483
+ `SHUTDOWN_DRAIN_SECONDS` (default 600), while the new pod serves traffic.
484
+ An idle pod exits at once. A run still going after that limit is marked
485
+ `lost`; if an `@`-mention started it, its status comment on GitHub says the
486
+ request was interrupted and should be repeated. The manifest's
487
+ `terminationGracePeriodSeconds` (720) must stay above the drain limit plus
488
+ the `preStop` delay.
489
+ - **Data at rest and retention.** `dbos.operation_outputs` holds every step's
490
+ output — prompts, model responses and full tool results — with no retention
491
+ limit. Pruning finished workflows is the operator's job; see
492
+ [Durable executor](security-deployment.md#durable-executor) for what is
493
+ stored and how to prune it with `DBOS.deleteWorkflow`.
494
+
495
+ **Enabling it on an existing deployment.** The grants changed, so apply them
496
+ before deploying: run `deploy/gke/bootstrap-database-iam.sh --check`, then
497
+ `deploy/gke/bootstrap-database-iam.sh`, then `deploy/gke/up.sh`. Without the
498
+ grants the new control-plane pod crash-loops with "permission denied" while
499
+ the old one keeps serving, and `up.sh` stops at the rollout. On a brand-new
500
+ project the order under "Database login" already covers it.
501
+
502
+ **Switching back** to the in-process executor: set `EXECUTOR` to `in-process`
503
+ in `deploy/kind-coding/manifests/overlays/gke-autopilot/control-plane.yaml`
504
+ and re-run `up.sh`. Runs in flight at that moment end `lost`.
505
+
506
+ ### Pod priority and headroom
507
+
508
+ The GKE overlay defines three PriorityClasses so that, when a node runs short,
509
+ the scheduler evicts the cheapest pods first instead of an arbitrary one:
510
+
511
+ | Class | Value | Used by | Preempts others |
512
+ | ---------------------- | ------- | ---------------------------------------- | --------------- |
513
+ | `wardby-control-plane` | 1000000 | control plane and coding proxy | yes |
514
+ | `wardby-coding-run` | 1000 | coding-run pods and the preflight canary | no |
515
+ | `wardby-headroom` | -10 | the `wardby-headroom` placeholder | no |
516
+
517
+ Coding runs get their class through `KUBERNETES_RUN_PRIORITY_CLASS` on the
518
+ control plane. GKE's own `system-*` classes still outrank all three: priority
519
+ decides who is evicted first, not whether anything can be.
520
+
521
+ `wardby-headroom` is a one-replica Deployment of the `pause` image that
522
+ reserves spare capacity, preferably on the control plane's node. A
523
+ higher-priority pod that cannot fit takes that capacity by evicting the
524
+ placeholder rather than a Wardby pod, and Autopilot then provisions a node for
525
+ the placeholder. Autopilot bills for its requests (250m CPU and 512 MiB of
526
+ memory), and it counts against the `wardby-coding` ResourceQuota. To disable
527
+ it, set `replicas: 0` in
528
+ `deploy/kind-coding/manifests/overlays/gke-autopilot/priority.yaml` and re-run
529
+ `up.sh`; raise its requests to reserve more.
530
+
531
+ A PriorityClass's value and preemption policy cannot be changed in place:
532
+ delete the class and re-run `up.sh` to change them.
533
+
534
+ ### Coding-run services
535
+
536
+ A coding run whose agent allows [services](coding-services.md) waits for each
537
+ service's sidecar to become ready before its pod starts the coding agent, up
538
+ to `KUBERNETES_READY_TIMEOUT_MS` (default 120000, i.e. 120 seconds) on the
539
+ control plane. Pulling a service's image adds to that wait, so the first run
540
+ that schedules onto a fresh node — one that has never pulled the image before
541
+ — can need more of the bound than a run on a node that already has it
542
+ cached. Raise `KUBERNETES_READY_TIMEOUT_MS` if you see runs fail
543
+ `service_unready` only on new nodes.
544
+
545
+ ### Roll back
546
+
547
+ Images are pinned by digest, so undoing a rollout restores exactly what ran
548
+ before. `up.sh` prints the previous digests at the end of every deploy.
549
+
550
+ ```sh
551
+ kubectl -n wardby-coding rollout undo deploy/wardby-control-plane
552
+ kubectl -n wardby-coding rollout undo deploy/wardby-coding-proxy
553
+ ```
554
+
555
+ `rollout undo` restores only the Deployments' images and pod templates — there
556
+ is no password path to fall back to any more, so a rollback is safe only for
557
+ a version that still speaks IAM login. It does not touch any NetworkPolicy: a
558
+ change to the database egress rules themselves is not undone by `rollout
559
+ undo`.
560
+
561
+ Database migrations only go forward: a rollback does not undo a schema change.
562
+ That is safe while every migration is additive (new tables, nullable columns,
563
+ indexes), which is the rule for this repository. Secrets are not part of a
564
+ rollout either; they come from Secret Manager whichever version is running.
565
+
566
+ Useful diagnostics:
567
+
568
+ ```sh
569
+ kubectl -n wardby-coding get events --sort-by=.lastTimestamp
570
+ kubectl -n wardby-coding logs deploy/wardby-control-plane -c control-plane
571
+ kubectl -n wardby-coding logs deploy/wardby-coding-proxy
572
+ terraform -chdir=deploy/gke plan
573
+ ```
574
+
575
+ ### Rotate a secret
576
+
577
+ Add a version in Secret Manager, force both ExternalSecrets to sync and wait
578
+ for them, then restart both Deployments. The names below assume the default
579
+ `name_prefix` of `wardby`; substitute yours if you changed it.
580
+
581
+ ```sh
582
+ printf '%s' "$NEW_VALUE" | gcloud secrets versions add wardby-openai-api-key --project=YOUR_PROJECT_ID --data-file=-
583
+ KUBE_CONTEXT="$(kubectl config current-context)" NAMESPACE=wardby-coding bash -c 'source deploy/gke/lib-secrets.sh && wait_external_secrets_synced 120s wardby-coding-proxy-env wardby-control-plane-env' && \
584
+ kubectl -n wardby-coding rollout restart deploy/wardby-coding-proxy deploy/wardby-control-plane
585
+ ```
586
+
587
+ Pods read their environment only at start, hence the restart. The LLM API
588
+ keys and the database URL are read by both Deployments; the other secrets only
589
+ by the control plane. Do **not** rotate `SECRET_APP_KEY` this way: it encrypts
590
+ credentials already stored in the database, and a new key leaves them
591
+ unreadable.
592
+
593
+ See [Observability](observability.md) for Prometheus, Grafana, and cloud metric
594
+ collection options.
595
+
596
+ ## 9. Teardown
597
+
598
+ The cluster, Cloud SQL and the Secret Manager secrets use deletion protection. Disable all three flags and
599
+ apply that change before destroying:
600
+
601
+ ```hcl
602
+ deletion_protection = false
603
+ cluster_deletion_protection = false
604
+ secrets_deletion_protection = false
605
+ ```
606
+
607
+ ```sh
608
+ terraform -chdir=deploy/gke apply
609
+ terraform -chdir=deploy/gke destroy
610
+ ```
611
+
612
+ The static address, Certificate Manager resources, DNS records, and Cloud Armor
613
+ policy were created outside Terraform and must be removed separately after the
614
+ Gateway is gone. Review the project for retained Artifact Registry images and
615
+ remote Terraform state before deleting the project.