@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.
- package/.env.example +34 -4
- package/README.md +41 -4
- package/dist/claude-coding-worker/driver.d.ts +6 -1
- package/dist/claude-coding-worker/driver.js +27 -1
- package/dist/claude-coding-worker/main.js +2 -0
- package/dist/claude-coding-worker/tool-socket.d.ts +9 -0
- package/dist/claude-coding-worker/tool-socket.js +26 -0
- package/dist/cli-help.d.ts +1 -1
- package/dist/cli-help.js +13 -4
- package/dist/cli.d.ts +1 -1
- package/dist/cli.js +219 -29
- package/dist/coding/collect-exclude.d.ts +25 -0
- package/dist/coding/collect-exclude.js +76 -0
- package/dist/coding/profile.d.ts +67 -22
- package/dist/coding/profile.js +58 -25
- package/dist/coding/protected-path-wording.d.ts +24 -0
- package/dist/coding/protected-path-wording.js +55 -0
- package/dist/coding/protected-paths.d.ts +31 -0
- package/dist/coding/protected-paths.js +48 -0
- package/dist/coding/protocol.d.ts +75 -4
- package/dist/coding/protocol.js +107 -7
- package/dist/coding/registry/adapters.d.ts +2 -0
- package/dist/coding/registry/adapters.js +9 -0
- package/dist/coding/registry/allowlist.d.ts +9 -0
- package/dist/coding/registry/allowlist.js +28 -0
- package/dist/coding/registry/json-scan.d.ts +55 -0
- package/dist/coding/registry/json-scan.js +191 -0
- package/dist/coding/registry/lockfiles.d.ts +6 -0
- package/dist/coding/registry/lockfiles.js +89 -0
- package/dist/coding/registry/npm-lockfile.d.ts +9 -0
- package/dist/coding/registry/npm-lockfile.js +73 -0
- package/dist/coding/registry/npm-plan.d.ts +29 -0
- package/dist/coding/registry/npm-plan.js +289 -0
- package/dist/coding/registry/npm.d.ts +7 -0
- package/dist/coding/registry/npm.js +252 -0
- package/dist/coding/registry/pypi.d.ts +4 -0
- package/dist/coding/registry/pypi.js +210 -0
- package/dist/coding/registry/report.d.ts +37 -0
- package/dist/coding/registry/report.js +31 -0
- package/dist/coding/registry/token.d.ts +4 -0
- package/dist/coding/registry/token.js +7 -0
- package/dist/coding/registry/types.d.ts +286 -0
- package/dist/coding/registry/types.js +19 -0
- package/dist/coding/registry/worker-config.d.ts +14 -0
- package/dist/coding/registry/worker-config.js +31 -0
- package/dist/coding/services/builtins.d.ts +22 -0
- package/dist/coding/services/builtins.js +85 -0
- package/dist/coding/services/catalog.d.ts +443 -0
- package/dist/coding/services/catalog.js +159 -0
- package/dist/coding/services/declaration.d.ts +11 -0
- package/dist/coding/services/declaration.js +114 -0
- package/dist/coding/services/note.d.ts +8 -0
- package/dist/coding/services/note.js +15 -0
- package/dist/coding/services/resolve.d.ts +25 -0
- package/dist/coding/services/resolve.js +50 -0
- package/dist/coding/services/wording.d.ts +25 -0
- package/dist/coding/services/wording.js +70 -0
- package/dist/coding-proxy/main.js +24 -4
- package/dist/coding-worker/artifact.d.ts +6 -0
- package/dist/coding-worker/debug-trace.d.ts +37 -0
- package/dist/coding-worker/debug-trace.js +117 -0
- package/dist/coding-worker/driver.d.ts +13 -1
- package/dist/coding-worker/driver.js +102 -13
- package/dist/coding-worker/errors.js +8 -0
- package/dist/coding-worker/main.js +10 -2
- package/dist/coding-worker/sdk.d.ts +2 -2
- package/dist/coding-worker/sdk.js +7 -2
- package/dist/coding-worker/types.d.ts +3 -0
- package/dist/config/providers.d.ts +41 -0
- package/dist/config/providers.js +66 -0
- package/dist/core/budget-groups.d.ts +108 -5
- package/dist/core/budget-groups.js +125 -19
- package/dist/core/budget-wording.d.ts +22 -0
- package/dist/core/budget-wording.js +55 -0
- package/dist/core/datastores.js +18 -2
- package/dist/core/db.d.ts +5 -1
- package/dist/core/db.js +8 -2
- package/dist/core/dispatch.d.ts +33 -6
- package/dist/core/dispatch.js +260 -43
- package/dist/core/engine-native.js +27 -10
- package/dist/core/grants.d.ts +86 -0
- package/dist/core/grants.js +126 -0
- package/dist/core/host-events.d.ts +49 -0
- package/dist/core/host-events.js +257 -0
- package/dist/core/host-identity-links.d.ts +54 -0
- package/dist/core/host-identity-links.js +189 -0
- package/dist/core/host-status.d.ts +61 -0
- package/dist/core/host-status.js +211 -0
- package/dist/core/in-flight-runs.d.ts +8 -0
- package/dist/core/in-flight-runs.js +56 -0
- package/dist/core/provider-wording.d.ts +12 -0
- package/dist/core/provider-wording.js +44 -0
- package/dist/core/reconciler.d.ts +38 -2
- package/dist/core/reconciler.js +86 -2
- package/dist/core/repo-access.d.ts +99 -0
- package/dist/core/repo-access.js +136 -0
- package/dist/core/review-host-checks.d.ts +11 -0
- package/dist/core/review-host-checks.js +41 -0
- package/dist/core/review-host-tools.d.ts +47 -0
- package/dist/core/review-host-tools.js +347 -0
- package/dist/core/run-heartbeat.d.ts +27 -0
- package/dist/core/run-heartbeat.js +54 -0
- package/dist/core/runner.d.ts +30 -8
- package/dist/core/runner.js +237 -40
- package/dist/core/secrets.d.ts +11 -2
- package/dist/core/secrets.js +25 -6
- package/dist/core/subagent-memory-tools.d.ts +1 -1
- package/dist/core/subagent-memory-tools.js +16 -3
- package/dist/core/tool-admin.d.ts +81 -0
- package/dist/core/tool-admin.js +129 -0
- package/dist/core/tool-names.d.ts +42 -0
- package/dist/core/tool-names.js +64 -0
- package/dist/core/untrusted-content.d.ts +32 -0
- package/dist/core/untrusted-content.js +72 -0
- package/dist/core/webhooks.d.ts +8 -1
- package/dist/core/webhooks.js +23 -2
- package/dist/generated/prisma/browser.d.ts +100 -0
- package/dist/generated/prisma/client.d.ts +100 -0
- package/dist/generated/prisma/commonInputTypes.d.ts +30 -0
- package/dist/generated/prisma/enums.d.ts +6 -0
- package/dist/generated/prisma/enums.js +6 -1
- package/dist/generated/prisma/internal/class.d.ts +143 -0
- package/dist/generated/prisma/internal/class.js +4 -4
- package/dist/generated/prisma/internal/prismaNamespace.d.ts +1746 -577
- package/dist/generated/prisma/internal/prismaNamespace.js +179 -6
- package/dist/generated/prisma/internal/prismaNamespaceBrowser.d.ts +186 -0
- package/dist/generated/prisma/internal/prismaNamespaceBrowser.js +179 -6
- package/dist/generated/prisma/models/Agent.d.ts +294 -1
- package/dist/generated/prisma/models/AgentRepository.d.ts +1425 -0
- package/dist/generated/prisma/models/AgentRepository.js +1 -0
- package/dist/generated/prisma/models/AgentTool.d.ts +95 -1
- package/dist/generated/prisma/models/AuthUser.d.ts +56 -1
- package/dist/generated/prisma/models/CodingAgentProfile.d.ts +258 -7
- package/dist/generated/prisma/models/CodingProxySession.d.ts +123 -2
- package/dist/generated/prisma/models/CodingRun.d.ts +1276 -95
- package/dist/generated/prisma/models/CodingService.d.ts +1348 -0
- package/dist/generated/prisma/models/CodingService.js +1 -0
- package/dist/generated/prisma/models/HostEventDelivery.d.ts +946 -0
- package/dist/generated/prisma/models/HostEventDelivery.js +1 -0
- package/dist/generated/prisma/models/HostIdentity.d.ts +1232 -0
- package/dist/generated/prisma/models/HostIdentity.js +1 -0
- package/dist/generated/prisma/models/HostIdentityLinkRequest.d.ts +1473 -0
- package/dist/generated/prisma/models/HostIdentityLinkRequest.js +1 -0
- package/dist/generated/prisma/models/Principal.d.ts +455 -0
- package/dist/generated/prisma/models/RegistryAllowance.d.ts +1148 -0
- package/dist/generated/prisma/models/RegistryAllowance.js +1 -0
- package/dist/generated/prisma/models/RegistryApprovedVersion.d.ts +1219 -0
- package/dist/generated/prisma/models/RegistryApprovedVersion.js +1 -0
- package/dist/generated/prisma/models/RegistryFetch.d.ts +1428 -0
- package/dist/generated/prisma/models/RegistryFetch.js +1 -0
- package/dist/generated/prisma/models/RegistryPlanRefusal.d.ts +1294 -0
- package/dist/generated/prisma/models/RegistryPlanRefusal.js +1 -0
- package/dist/generated/prisma/models/RegistryVersionFact.d.ts +1085 -0
- package/dist/generated/prisma/models/RegistryVersionFact.js +1 -0
- package/dist/generated/prisma/models/ResourceGrant.d.ts +1437 -0
- package/dist/generated/prisma/models/ResourceGrant.js +1 -0
- package/dist/generated/prisma/models/Run.d.ts +389 -1
- package/dist/generated/prisma/models/RunHostCheck.d.ts +1239 -0
- package/dist/generated/prisma/models/RunHostCheck.js +1 -0
- package/dist/generated/prisma/models/RunHostStatus.d.ts +1315 -0
- package/dist/generated/prisma/models/RunHostStatus.js +1 -0
- package/dist/generated/prisma/models/Tool.d.ts +15 -3
- package/dist/generated/prisma/models.d.ts +13 -0
- package/dist/help/build.d.ts +1 -0
- package/dist/help/build.js +9 -0
- package/dist/help/catalog.d.ts +24 -0
- package/dist/help/catalog.js +160 -0
- package/dist/help/cli.d.ts +2 -0
- package/dist/help/cli.js +65 -0
- package/dist/help/runtime.d.ts +3 -0
- package/dist/help/runtime.js +44 -0
- package/dist/help/search.d.ts +9 -0
- package/dist/help/search.js +104 -0
- package/dist/help-index.json +660 -0
- package/dist/import/cli-args.js +3 -2
- package/dist/import/create.d.ts +5 -0
- package/dist/import/create.js +67 -13
- package/dist/import/index.js +19 -9
- package/dist/import/neutral-schema.d.ts +11 -11
- package/dist/mcp/auth/access.d.ts +70 -0
- package/dist/mcp/auth/access.js +58 -0
- package/dist/mcp/auth/grants-cli.d.ts +149 -0
- package/dist/mcp/auth/grants-cli.js +518 -0
- package/dist/mcp/auth/host-account-cli.d.ts +2 -0
- package/dist/mcp/auth/host-account-cli.js +47 -0
- package/dist/mcp/auth/ownership.d.ts +26 -52
- package/dist/mcp/auth/ownership.js +19 -14
- package/dist/mcp/auth/repo-authorization.d.ts +22 -0
- package/dist/mcp/auth/repo-authorization.js +51 -0
- package/dist/mcp/auth/resource-server.d.ts +31 -2
- package/dist/mcp/auth/resource-server.js +84 -3
- package/dist/mcp/auth/self-hosted/browser.js +2 -2
- package/dist/mcp/auth/self-hosted/cli.js +47 -13
- package/dist/mcp/auth/self-hosted/credentials.d.ts +20 -2
- package/dist/mcp/auth/self-hosted/credentials.js +62 -4
- package/dist/mcp/auth/self-hosted/session.d.ts +2 -1
- package/dist/mcp/context.d.ts +27 -1
- package/dist/mcp/errors.d.ts +25 -6
- package/dist/mcp/errors.js +98 -0
- package/dist/mcp/host-events/github-ingress.d.ts +36 -0
- package/dist/mcp/host-events/github-ingress.js +92 -0
- package/dist/mcp/host-events/github-user-callback.d.ts +18 -0
- package/dist/mcp/host-events/github-user-callback.js +50 -0
- package/dist/mcp/index.d.ts +8 -1
- package/dist/mcp/index.js +106 -16
- package/dist/mcp/server.js +27 -9
- package/dist/mcp/tools/agents.js +446 -48
- package/dist/mcp/tools/budget-groups.js +3 -3
- package/dist/mcp/tools/datastore.js +22 -15
- package/dist/mcp/tools/grants.d.ts +2 -0
- package/dist/mcp/tools/grants.js +239 -0
- package/dist/mcp/tools/help.d.ts +5 -0
- package/dist/mcp/tools/help.js +67 -0
- package/dist/mcp/tools/host-accounts.d.ts +2 -0
- package/dist/mcp/tools/host-accounts.js +114 -0
- package/dist/mcp/tools/memory.d.ts +7 -1
- package/dist/mcp/tools/memory.js +5 -5
- package/dist/mcp/tools/repositories.d.ts +2 -0
- package/dist/mcp/tools/repositories.js +181 -0
- package/dist/mcp/tools/runs.d.ts +6 -0
- package/dist/mcp/tools/runs.js +60 -6
- package/dist/mcp/tools/scheduling.js +3 -6
- package/dist/mcp/tools/secrets.js +18 -6
- package/dist/mcp/tools/services.d.ts +2 -0
- package/dist/mcp/tools/services.js +222 -0
- package/dist/mcp/tools/subagents.js +52 -16
- package/dist/mcp/tools/tools.d.ts +41 -0
- package/dist/mcp/tools/tools.js +201 -40
- package/dist/mcp/tools/trigger.js +36 -6
- package/dist/mcp/tools/webhooks.js +15 -4
- package/dist/mcp/transport/streamable-http.d.ts +10 -0
- package/dist/mcp/transport/streamable-http.js +32 -2
- package/dist/providers/auth/delegating.d.ts +19 -0
- package/dist/providers/auth/delegating.js +71 -0
- package/dist/providers/auth/index.d.ts +2 -0
- package/dist/providers/auth/index.js +11 -0
- package/dist/providers/auth/self-hosted.d.ts +2 -1
- package/dist/providers/auth/self-hosted.js +16 -4
- package/dist/providers/auth/types.d.ts +6 -0
- package/dist/providers/coding-proxy/memory-ledger.d.ts +3 -0
- package/dist/providers/coding-proxy/memory-ledger.js +21 -2
- package/dist/providers/coding-proxy/metering.d.ts +4 -0
- package/dist/providers/coding-proxy/metering.js +28 -2
- package/dist/providers/coding-proxy/prisma-ledger.d.ts +17 -0
- package/dist/providers/coding-proxy/prisma-ledger.js +50 -5
- package/dist/providers/coding-proxy/proxy.d.ts +13 -0
- package/dist/providers/coding-proxy/proxy.js +479 -18
- package/dist/providers/coding-proxy/registry/audit.d.ts +131 -0
- package/dist/providers/coding-proxy/registry/audit.js +380 -0
- package/dist/providers/coding-proxy/registry/bounded-fetch.d.ts +16 -0
- package/dist/providers/coding-proxy/registry/bounded-fetch.js +77 -0
- package/dist/providers/coding-proxy/registry/plan.d.ts +58 -0
- package/dist/providers/coding-proxy/registry/plan.js +304 -0
- package/dist/providers/coding-proxy/registry/prisma-store.d.ts +34 -0
- package/dist/providers/coding-proxy/registry/prisma-store.js +151 -0
- package/dist/providers/coding-proxy/registry/service.d.ts +213 -0
- package/dist/providers/coding-proxy/registry/service.js +1137 -0
- package/dist/providers/coding-proxy/registry/store.d.ts +127 -0
- package/dist/providers/coding-proxy/registry/store.js +70 -0
- package/dist/providers/coding-proxy/runtime.d.ts +17 -0
- package/dist/providers/coding-proxy/runtime.js +63 -0
- package/dist/providers/coding-proxy/secure-fetch.js +0 -1
- package/dist/providers/coding-proxy/server.d.ts +11 -0
- package/dist/providers/coding-proxy/server.js +163 -0
- package/dist/providers/coding-proxy/types.d.ts +15 -0
- package/dist/providers/engine/types.d.ts +10 -0
- package/dist/providers/executor/build.d.ts +2 -2
- package/dist/providers/executor/composition.d.ts +3 -0
- package/dist/providers/executor/composition.js +19 -0
- package/dist/providers/executor/container.d.ts +105 -3
- package/dist/providers/executor/container.js +296 -20
- package/dist/providers/executor/dbos.d.ts +2 -3
- package/dist/providers/executor/in-process.d.ts +2 -3
- package/dist/providers/executor/routing.d.ts +7 -0
- package/dist/providers/executor/routing.js +13 -0
- package/dist/providers/executor/types.d.ts +22 -0
- package/dist/providers/jobs/claude-tool-setup.d.ts +23 -0
- package/dist/providers/jobs/claude-tool-setup.js +50 -0
- package/dist/providers/jobs/collect-prune.d.ts +7 -0
- package/dist/providers/jobs/collect-prune.js +27 -0
- package/dist/providers/jobs/docker-isolation.d.ts +48 -3
- package/dist/providers/jobs/docker-isolation.js +213 -22
- package/dist/providers/jobs/docker-services.d.ts +35 -0
- package/dist/providers/jobs/docker-services.js +191 -0
- package/dist/providers/jobs/docker.d.ts +49 -2
- package/dist/providers/jobs/docker.js +279 -32
- package/dist/providers/jobs/fake-kubernetes-api.d.ts +1 -0
- package/dist/providers/jobs/fake-kubernetes-api.js +12 -3
- package/dist/providers/jobs/kubernetes-isolation.d.ts +17 -2
- package/dist/providers/jobs/kubernetes-isolation.js +238 -57
- package/dist/providers/jobs/kubernetes-platform.d.ts +10 -4
- package/dist/providers/jobs/kubernetes-platform.js +11 -5
- package/dist/providers/jobs/kubernetes-preflight.js +3 -0
- package/dist/providers/jobs/kubernetes.d.ts +21 -2
- package/dist/providers/jobs/kubernetes.js +112 -21
- package/dist/providers/jobs/types.d.ts +18 -0
- package/dist/providers/llm/anthropic.js +6 -2
- package/dist/providers/llm/bedrock.js +2 -1
- package/dist/providers/llm/claude-messages.d.ts +5 -1
- package/dist/providers/llm/claude-messages.js +1 -0
- package/dist/providers/llm/claude-provider.d.ts +3 -1
- package/dist/providers/llm/claude-provider.js +5 -1
- package/dist/providers/llm/index.d.ts +1 -1
- package/dist/providers/llm/index.js +1 -1
- package/dist/providers/llm/pricing-anthropic.d.ts +10 -0
- package/dist/providers/llm/pricing-anthropic.js +32 -14
- package/dist/providers/llm/pricing-bedrock-claude.d.ts +8 -0
- package/dist/providers/llm/pricing-bedrock-claude.js +9 -0
- package/dist/providers/llm/routing.d.ts +10 -1
- package/dist/providers/llm/routing.js +18 -0
- package/dist/providers/llm/types.d.ts +12 -0
- package/dist/providers/llm/types.js +8 -1
- package/dist/providers/review-host/diff-lines.d.ts +16 -0
- package/dist/providers/review-host/diff-lines.js +59 -0
- package/dist/providers/review-host/github-events.d.ts +6 -0
- package/dist/providers/review-host/github-events.js +180 -0
- package/dist/providers/review-host/github-user-auth.d.ts +38 -0
- package/dist/providers/review-host/github-user-auth.js +128 -0
- package/dist/providers/review-host/github.d.ts +57 -0
- package/dist/providers/review-host/github.js +567 -0
- package/dist/providers/review-host/index.d.ts +12 -0
- package/dist/providers/review-host/index.js +30 -0
- package/dist/providers/review-host/review-format.d.ts +19 -0
- package/dist/providers/review-host/review-format.js +59 -0
- package/dist/providers/review-host/types.d.ts +284 -0
- package/dist/providers/review-host/types.js +24 -0
- package/dist/providers/vcs/git.d.ts +16 -6
- package/dist/providers/vcs/git.js +77 -13
- package/dist/providers/vcs/github.d.ts +74 -3
- package/dist/providers/vcs/github.js +195 -15
- package/dist/providers/vcs/types.d.ts +42 -4
- package/dist/quickstart/index.js +13 -2
- package/dist/sandbox/fetch-policy.d.ts +20 -2
- package/dist/sandbox/fetch-policy.js +64 -3
- package/dist/sandbox/host-functions.d.ts +9 -1
- package/dist/sandbox/host-functions.js +12 -5
- package/dist/serve.js +1 -1
- package/dist/wardby-bin.js +6 -0
- package/docs/README.md +30 -0
- package/docs/architecture-runtime.md +90 -0
- package/docs/assets/brand/wardby-icon-512.png +0 -0
- package/docs/assets/brand/wardby-icon.svg +16 -0
- package/docs/assets/brand/wardby-mascot-profile-512.png +0 -0
- package/docs/assets/brand/wardby-mascot.png +0 -0
- package/docs/assets/brand/wardby-mascot.svg +5 -0
- package/docs/assets/wardby-workflow.svg +106 -0
- package/docs/code-review-agents.md +481 -0
- package/docs/coding-agent-setup.md +172 -0
- package/docs/coding-packages.md +455 -0
- package/docs/coding-services.md +300 -0
- package/docs/coding-worker-byo-images.md +98 -0
- package/docs/coding-worker-isolation.md +1061 -0
- package/docs/getting-started-gke.md +615 -0
- package/docs/getting-started-identity-provider.md +308 -0
- package/docs/getting-started.md +133 -0
- package/docs/observability.md +53 -0
- package/docs/release-verification.md +66 -0
- package/docs/security-deployment.md +657 -0
- package/help/code-review-agents.md +31 -0
- package/help/coding-packages.md +31 -0
- package/help/coding-services.md +71 -0
- package/help/creating-agents.md +68 -0
- package/help/deploy-gke.md +39 -0
- package/help/deployment-targets.md +39 -0
- package/help/errors/budget-group-exhausted.md +25 -0
- package/help/errors/docker-isolation-unsupported.md +26 -0
- package/help/errors/protected-path.md +45 -0
- package/help/errors/repo-access.md +27 -0
- package/help/errors/service-declaration-invalid.md +27 -0
- package/help/errors/service-declaration-unavailable.md +25 -0
- package/help/errors/service-launcher-unsupported.md +30 -0
- package/help/errors/service-not-allowed.md +27 -0
- package/help/errors/service-unknown.md +23 -0
- package/help/errors/service-unready.md +49 -0
- package/help/getting-started.md +31 -0
- package/help/github.md +30 -0
- package/help/identity-and-access.md +39 -0
- package/help/mcp.md +30 -0
- package/help/native-capabilities.md +30 -0
- package/help/observability.md +41 -0
- package/help/operating-agents.md +30 -0
- package/help/security.md +27 -0
- package/help/troubleshooting/budgets.md +26 -0
- package/help/troubleshooting/coding-workers.md +47 -0
- package/help/troubleshooting/repository-access.md +27 -0
- package/package.json +12 -3
- package/prisma/migrations/20260925010000_coding_collect_exclude/migration.sql +8 -0
- package/prisma/migrations/20260925015000_allowed_egress_default/migration.sql +8 -0
- package/prisma/migrations/20260925020000_coding_package_registry/migration.sql +55 -0
- package/prisma/migrations/20260925030000_tool_name_per_owner/migration.sql +11 -0
- package/prisma/migrations/20260926010000_run_trigger_host_event/migration.sql +7 -0
- package/prisma/migrations/20260926020000_code_review_hosts/migration.sql +53 -0
- package/prisma/migrations/20260926030000_auth_user_roles/migration.sql +9 -0
- package/prisma/migrations/20260926040000_agent_effort/migration.sql +6 -0
- package/prisma/migrations/20260926050000_registry_lockfile_plan/migration.sql +32 -0
- package/prisma/migrations/20260926050000_repo_access_authorization/migration.sql +82 -0
- package/prisma/migrations/20260926060000_registry_plan_refusal/migration.sql +19 -0
- package/prisma/migrations/20260926100000_registry_plan_refusal_published_at/migration.sql +5 -0
- package/prisma/migrations/20260926190000_run_host_status/migration.sql +18 -0
- package/prisma/migrations/20260926210000_run_host_status_at_dispatch/migration.sql +5 -0
- package/prisma/migrations/20260927010000_resource_grants/migration.sql +72 -0
- package/prisma/migrations/20260927020000_proxy_session_budget_exhausted/migration.sql +3 -0
- package/prisma/migrations/20260927030000_coding_debug_trace/migration.sql +4 -0
- package/prisma/migrations/20260927040000_proxy_session_upstream_failure/migration.sql +3 -0
- package/prisma/migrations/20260927050000_coding_run_services/migration.sql +74 -0
- package/prisma/migrations/20260928000000_coding_run_tool_image/migration.sql +5 -0
- 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.
|