@wardby/cli 0.5.2 → 0.6.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 +33 -0
- package/README.md +25 -14
- package/dist/claude-coding-worker/context.d.ts +22 -0
- package/dist/claude-coding-worker/context.js +92 -0
- package/dist/claude-coding-worker/driver.d.ts +7 -1
- package/dist/claude-coding-worker/driver.js +38 -5
- package/dist/claude-coding-worker/sdk.js +13 -4
- package/dist/cli-help.d.ts +1 -1
- package/dist/cli-help.js +5 -2
- package/dist/cli-native-mode.d.ts +9 -0
- package/dist/cli-native-mode.js +20 -0
- package/dist/cli.d.ts +3 -1
- package/dist/cli.js +141 -5
- package/dist/coding/claude-context.d.ts +45 -0
- package/dist/coding/claude-context.js +434 -0
- package/dist/coding/local-git.js +9 -1
- package/dist/coding/profile.d.ts +12 -0
- package/dist/coding/profile.js +6 -0
- package/dist/coding/protocol.d.ts +104 -0
- package/dist/coding/protocol.js +87 -3
- package/dist/coding/registry/pypi.d.ts +25 -1
- package/dist/coding/registry/pypi.js +140 -16
- package/dist/coding/registry/types.d.ts +17 -3
- package/dist/coding-worker/artifact.d.ts +8 -0
- package/dist/coding-worker/artifact.js +2 -2
- package/dist/coding-worker/driver.js +2 -0
- package/dist/coding-worker/sdk.d.ts +6 -0
- package/dist/coding-worker/sdk.js +1 -0
- package/dist/coding-worker/skills.d.ts +23 -0
- package/dist/coding-worker/skills.js +117 -0
- package/dist/coding-worker/types.d.ts +2 -0
- package/dist/config/github-allowed-accounts.d.ts +15 -0
- package/dist/config/github-allowed-accounts.js +36 -0
- package/dist/config/low-power.d.ts +12 -0
- package/dist/config/low-power.js +18 -0
- package/dist/config/mobile.d.ts +12 -0
- package/dist/config/mobile.js +18 -0
- package/dist/config/providers.d.ts +56 -2
- package/dist/config/providers.js +92 -6
- package/dist/core/db.d.ts +16 -6
- package/dist/core/db.js +12 -10
- package/dist/core/dispatch.d.ts +11 -0
- package/dist/core/dispatch.js +36 -1
- package/dist/core/engine-native.js +9 -5
- package/dist/core/host-events.d.ts +49 -3
- package/dist/core/host-events.js +289 -15
- package/dist/core/http-runtime.js +13 -6
- package/dist/core/issue-bridge.js +42 -2
- package/dist/core/issue-events.js +12 -1
- package/dist/core/issue-status.js +14 -3
- package/dist/core/merge-order-check.d.ts +129 -0
- package/dist/core/merge-order-check.js +336 -0
- package/dist/core/notification-dispatcher.d.ts +70 -0
- package/dist/core/notification-dispatcher.js +368 -0
- package/dist/core/notification-templates.d.ts +25 -0
- package/dist/core/notification-templates.js +105 -0
- package/dist/core/notifications.d.ts +10 -0
- package/dist/core/notifications.js +38 -0
- package/dist/core/pg-auth.d.ts +22 -0
- package/dist/core/pg-auth.js +152 -0
- package/dist/core/power-composition.d.ts +16 -0
- package/dist/core/power-composition.js +37 -0
- package/dist/core/power-idle.d.ts +14 -0
- package/dist/core/power-idle.js +30 -0
- package/dist/core/power.d.ts +71 -0
- package/dist/core/power.js +202 -0
- package/dist/core/reconciler.d.ts +4 -2
- package/dist/core/reconciler.js +29 -5
- package/dist/core/related-pull-requests.d.ts +48 -4
- package/dist/core/related-pull-requests.js +113 -11
- package/dist/core/repo-access.d.ts +15 -0
- package/dist/core/repo-access.js +66 -0
- package/dist/core/review-fix.d.ts +37 -0
- package/dist/core/review-fix.js +207 -6
- package/dist/core/review-host-tools.d.ts +20 -0
- package/dist/core/review-host-tools.js +58 -2
- package/dist/core/runner.d.ts +336 -3
- package/dist/core/runner.js +894 -317
- package/dist/core/secret-broker-config.d.ts +103 -0
- package/dist/core/secret-broker-config.js +112 -0
- package/dist/core/secrets.d.ts +39 -4
- package/dist/core/secrets.js +148 -40
- package/dist/core/workflow-event-recorder.d.ts +14 -0
- package/dist/core/workflow-event-recorder.js +120 -0
- package/dist/core/workflow-events.d.ts +82 -0
- package/dist/core/workflow-events.js +44 -0
- package/dist/core/workflow-run-events.d.ts +16 -0
- package/dist/core/workflow-run-events.js +40 -0
- package/dist/generated/prisma/browser.d.ts +58 -0
- package/dist/generated/prisma/client.d.ts +58 -0
- package/dist/generated/prisma/commonInputTypes.d.ts +150 -0
- package/dist/generated/prisma/enums.d.ts +27 -0
- package/dist/generated/prisma/enums.js +23 -0
- package/dist/generated/prisma/internal/class.d.ts +88 -0
- package/dist/generated/prisma/internal/class.js +4 -4
- package/dist/generated/prisma/internal/prismaNamespace.d.ts +772 -1
- package/dist/generated/prisma/internal/prismaNamespace.js +135 -4
- package/dist/generated/prisma/internal/prismaNamespaceBrowser.d.ts +139 -0
- package/dist/generated/prisma/internal/prismaNamespaceBrowser.js +135 -4
- package/dist/generated/prisma/models/Agent.d.ts +360 -1
- package/dist/generated/prisma/models/CodingAgentProfile.d.ts +73 -1
- package/dist/generated/prisma/models/CodingRun.d.ts +205 -1
- package/dist/generated/prisma/models/DeferredReview.d.ts +78 -1
- package/dist/generated/prisma/models/NativeGatewayCall.d.ts +1675 -0
- package/dist/generated/prisma/models/NativeGatewayCall.js +1 -0
- package/dist/generated/prisma/models/NativeGatewaySession.d.ts +1517 -0
- package/dist/generated/prisma/models/NativeGatewaySession.js +1 -0
- package/dist/generated/prisma/models/NativeWarmWorker.d.ts +1296 -0
- package/dist/generated/prisma/models/NativeWarmWorker.js +1 -0
- package/dist/generated/prisma/models/NotificationChannel.d.ts +1543 -0
- package/dist/generated/prisma/models/NotificationChannel.js +1 -0
- package/dist/generated/prisma/models/NotificationDelivery.d.ts +1502 -0
- package/dist/generated/prisma/models/NotificationDelivery.js +1 -0
- package/dist/generated/prisma/models/NotificationThread.d.ts +1090 -0
- package/dist/generated/prisma/models/NotificationThread.js +1 -0
- package/dist/generated/prisma/models/Run.d.ts +826 -1
- package/dist/generated/prisma/models/RunHostCheck.d.ts +36 -1
- package/dist/generated/prisma/models/Secret.d.ts +37 -1
- package/dist/generated/prisma/models/SecretBrokerChange.d.ts +1101 -0
- package/dist/generated/prisma/models/SecretBrokerChange.js +1 -0
- package/dist/generated/prisma/models/WorkflowEvent.d.ts +1422 -0
- package/dist/generated/prisma/models/WorkflowEvent.js +1 -0
- package/dist/generated/prisma/models.d.ts +8 -0
- package/dist/help/build.js +4 -2
- package/dist/help/catalog.d.ts +1 -1
- package/dist/help/catalog.js +31 -6
- package/dist/help-index.json +1492 -113
- package/dist/import/create.js +17 -0
- package/dist/import/neutral-schema.d.ts +6 -2
- package/dist/import/neutral-schema.js +2 -0
- package/dist/mcp/auth/access.d.ts +1 -0
- package/dist/mcp/auth/ownership.d.ts +1 -1
- package/dist/mcp/auth/repo-authorization.js +14 -0
- package/dist/mcp/auth/resource-server.d.ts +2 -1
- package/dist/mcp/auth/resource-server.js +7 -2
- package/dist/mcp/auth/self-hosted/browser.d.ts +1 -1
- package/dist/mcp/auth/self-hosted/browser.js +19 -6
- package/dist/mcp/context.d.ts +6 -0
- package/dist/mcp/host-events/github-ingress.d.ts +23 -1
- package/dist/mcp/host-events/github-ingress.js +68 -0
- package/dist/mcp/index.d.ts +13 -0
- package/dist/mcp/index.js +59 -10
- package/dist/mcp/tools/agents.js +83 -12
- package/dist/mcp/tools/notification-channels.d.ts +2 -0
- package/dist/mcp/tools/notification-channels.js +293 -0
- package/dist/mcp/tools/power.d.ts +2 -0
- package/dist/mcp/tools/power.js +77 -0
- package/dist/mcp/tools/runs.d.ts +0 -6
- package/dist/mcp/tools/runs.js +7 -2
- package/dist/mcp/tools/secret-broker-compat.d.ts +12 -0
- package/dist/mcp/tools/secret-broker-compat.js +23 -0
- package/dist/mcp/tools/secret-elicitation-form.d.ts +22 -2
- package/dist/mcp/tools/secret-elicitation-form.js +200 -10
- package/dist/mcp/tools/secret-elicitation-server.js +8 -1
- package/dist/mcp/tools/secret-elicitation.d.ts +38 -3
- package/dist/mcp/tools/secret-elicitation.js +110 -12
- package/dist/mcp/tools/secrets.js +184 -7
- package/dist/mcp/tools/tools.d.ts +1 -1
- package/dist/mcp/tools/tools.js +22 -2
- package/dist/native-worker/composition.d.ts +23 -0
- package/dist/native-worker/composition.js +77 -0
- package/dist/native-worker/docker-isolation.d.ts +65 -0
- package/dist/native-worker/docker-isolation.js +183 -0
- package/dist/native-worker/docker-launcher.d.ts +74 -0
- package/dist/native-worker/docker-launcher.js +237 -0
- package/dist/native-worker/gateway-server.d.ts +39 -0
- package/dist/native-worker/gateway-server.js +315 -0
- package/dist/native-worker/gateway.d.ts +44 -0
- package/dist/native-worker/gateway.js +197 -0
- package/dist/native-worker/http-server.d.ts +22 -0
- package/dist/native-worker/http-server.js +109 -0
- package/dist/native-worker/http-transport.d.ts +17 -0
- package/dist/native-worker/http-transport.js +88 -0
- package/dist/native-worker/kubernetes-isolation.d.ts +90 -0
- package/dist/native-worker/kubernetes-isolation.js +218 -0
- package/dist/native-worker/kubernetes-launcher.d.ts +102 -0
- package/dist/native-worker/kubernetes-launcher.js +335 -0
- package/dist/native-worker/launch.d.ts +45 -0
- package/dist/native-worker/launch.js +44 -0
- package/dist/native-worker/ledger.d.ts +109 -0
- package/dist/native-worker/ledger.js +192 -0
- package/dist/native-worker/loopback.d.ts +12 -0
- package/dist/native-worker/loopback.js +49 -0
- package/dist/native-worker/main.d.ts +7 -0
- package/dist/native-worker/main.js +74 -0
- package/dist/native-worker/protocol.d.ts +744 -0
- package/dist/native-worker/protocol.js +200 -0
- package/dist/native-worker/sandbox-executor.d.ts +68 -0
- package/dist/native-worker/sandbox-executor.js +210 -0
- package/dist/native-worker/stdio.d.ts +29 -0
- package/dist/native-worker/stdio.js +188 -0
- package/dist/native-worker/warm-delivery.d.ts +16 -0
- package/dist/native-worker/warm-delivery.js +33 -0
- package/dist/native-worker/warm-pool-ledger.d.ts +39 -0
- package/dist/native-worker/warm-pool-ledger.js +73 -0
- package/dist/native-worker/warm-pool.d.ts +107 -0
- package/dist/native-worker/warm-pool.js +283 -0
- package/dist/native-worker/worker.d.ts +17 -0
- package/dist/native-worker/worker.js +115 -0
- package/dist/providers/auth/authorization-server.d.ts +3 -0
- package/dist/providers/auth/index.js +2 -0
- package/dist/providers/auth/self-hosted.d.ts +13 -1
- package/dist/providers/auth/self-hosted.js +54 -4
- package/dist/providers/chat/fake.d.ts +46 -0
- package/dist/providers/chat/fake.js +39 -0
- package/dist/providers/chat/index.d.ts +4 -0
- package/dist/providers/chat/index.js +8 -0
- package/dist/providers/chat/slack.d.ts +37 -0
- package/dist/providers/chat/slack.js +105 -0
- package/dist/providers/chat/types.d.ts +42 -0
- package/dist/providers/chat/types.js +19 -0
- package/dist/providers/coding-proxy/mock-upstream.d.ts +33 -0
- package/dist/providers/coding-proxy/mock-upstream.js +116 -0
- package/dist/providers/coding-proxy/registry/prisma-store.d.ts +3 -0
- package/dist/providers/coding-proxy/registry/prisma-store.js +17 -0
- package/dist/providers/coding-proxy/registry/service.d.ts +4 -0
- package/dist/providers/coding-proxy/registry/service.js +20 -3
- package/dist/providers/coding-proxy/registry/store.d.ts +18 -1
- package/dist/providers/coding-proxy/registry/store.js +33 -0
- package/dist/providers/coding-proxy/runtime.js +12 -1
- package/dist/providers/coding-proxy/types.d.ts +2 -0
- package/dist/providers/engine/types.d.ts +19 -0
- package/dist/providers/engine/types.js +22 -0
- package/dist/providers/executor/composition.d.ts +24 -1
- package/dist/providers/executor/composition.js +39 -6
- package/dist/providers/executor/container.d.ts +24 -1
- package/dist/providers/executor/container.js +101 -11
- package/dist/providers/executor/dbos.d.ts +42 -0
- package/dist/providers/executor/dbos.js +118 -12
- package/dist/providers/executor/deferred.d.ts +56 -0
- package/dist/providers/executor/deferred.js +119 -0
- package/dist/providers/executor/routing.d.ts +37 -10
- package/dist/providers/executor/routing.js +73 -32
- package/dist/providers/executor/types.d.ts +12 -0
- package/dist/providers/jobs/docker.js +3 -3
- package/dist/providers/jobs/fake-kubernetes-api.d.ts +21 -2
- package/dist/providers/jobs/fake-kubernetes-api.js +42 -1
- package/dist/providers/jobs/kubernetes-api.d.ts +17 -1
- package/dist/providers/jobs/kubernetes-api.js +7 -0
- package/dist/providers/jobs/kubernetes-client.d.ts +10 -1
- package/dist/providers/jobs/kubernetes-client.js +52 -3
- package/dist/providers/jobs/kubernetes-dry-run-fixture.d.ts +6 -1
- package/dist/providers/jobs/kubernetes-dry-run-fixture.js +6 -3
- package/dist/providers/jobs/kubernetes-isolation.d.ts +16 -1
- package/dist/providers/jobs/kubernetes-isolation.js +58 -5
- package/dist/providers/jobs/kubernetes-platform.d.ts +4 -1
- package/dist/providers/jobs/kubernetes-platform.js +38 -1
- package/dist/providers/jobs/kubernetes-preflight.d.ts +2 -1
- package/dist/providers/jobs/kubernetes-preflight.js +4 -2
- package/dist/providers/jobs/kubernetes.d.ts +56 -7
- package/dist/providers/jobs/kubernetes.js +117 -36
- package/dist/providers/jobs/metadata-probes.d.ts +11 -0
- package/dist/providers/jobs/metadata-probes.js +13 -0
- package/dist/providers/jobs/types.d.ts +9 -0
- package/dist/providers/llm/openai.js +0 -1
- package/dist/providers/review-host/github.d.ts +11 -1
- package/dist/providers/review-host/github.js +50 -0
- package/dist/providers/review-host/types.d.ts +23 -0
- package/dist/providers/vcs/github.d.ts +32 -6
- package/dist/providers/vcs/github.js +74 -16
- package/dist/quickstart/coding-db.js +14 -2
- package/dist/quickstart/coding-doctor.d.ts +3 -0
- package/dist/quickstart/coding-doctor.js +3 -0
- package/dist/quickstart/coding-images.d.ts +9 -0
- package/dist/quickstart/coding-images.js +39 -0
- package/dist/quickstart/coding-seed.d.ts +20 -1
- package/dist/quickstart/coding-seed.js +96 -7
- package/dist/quickstart/coding.d.ts +2 -0
- package/dist/quickstart/coding.js +75 -3
- package/dist/quickstart/config.d.ts +2 -0
- package/dist/quickstart/config.js +4 -0
- package/dist/quickstart/images.d.ts +13 -0
- package/dist/quickstart/images.js +80 -12
- package/dist/quickstart/index.d.ts +0 -2
- package/dist/quickstart/index.js +10 -14
- package/dist/quickstart/manifests.d.ts +56 -0
- package/dist/quickstart/manifests.js +523 -0
- package/dist/quickstart/python-detect.d.ts +10 -0
- package/dist/quickstart/python-detect.js +32 -0
- package/dist/quickstart/repo-packages.d.ts +10 -0
- package/dist/quickstart/repo-packages.js +132 -0
- package/dist/quickstart/reviewer-prompt.d.ts +12 -0
- package/dist/quickstart/reviewer-prompt.js +84 -0
- package/dist/quickstart/scenarios.d.ts +19 -0
- package/dist/quickstart/scenarios.js +28 -0
- package/dist/quickstart/sync-reviewer-prompt.d.ts +1 -0
- package/dist/quickstart/sync-reviewer-prompt.js +10 -0
- package/dist/quickstart-images.json +1 -1
- package/dist/sandbox/brokered-fetch.d.ts +23 -0
- package/dist/sandbox/brokered-fetch.js +37 -0
- package/dist/sandbox/host-functions.d.ts +34 -0
- package/dist/sandbox/host-functions.js +144 -80
- package/dist/sandbox/prelude.js +7 -1
- package/dist/sandbox/run-in-sandbox.d.ts +19 -0
- package/dist/sandbox/run-in-sandbox.js +28 -10
- package/dist/sandbox/safe-fetch.d.ts +2 -0
- package/dist/sandbox/safe-fetch.js +3 -1
- package/dist/sandbox/secret-broker-sigv4.d.ts +2 -0
- package/dist/sandbox/secret-broker-sigv4.js +49 -0
- package/dist/sandbox/secret-broker.d.ts +34 -0
- package/dist/sandbox/secret-broker.js +162 -0
- package/dist/sandbox/user-tool.d.ts +14 -0
- package/dist/sandbox/user-tool.js +32 -0
- package/dist/serve.js +18 -0
- package/dist/viewer/api-schema.d.ts +214 -18
- package/dist/viewer/api-schema.js +22 -0
- package/dist/viewer/build-schemas.js +4 -1
- package/dist/viewer/event-bus.d.ts +6 -0
- package/dist/viewer/event-bus.js +16 -7
- package/dist/viewer/graph.d.ts +5 -2
- package/dist/viewer/graph.js +7 -2
- package/dist/viewer/http.d.ts +0 -1
- package/dist/viewer/http.js +2 -2
- package/dist/viewer/infra.js +25 -2
- package/dist/viewer/limits.d.ts +4 -0
- package/dist/viewer/limits.js +4 -0
- package/dist/viewer/openapi.d.ts +15 -0
- package/dist/viewer/openapi.js +172 -0
- package/docs/README.md +7 -0
- package/docs/agent-recipes.md +57 -8
- package/docs/architecture-runtime.md +16 -1
- package/docs/code-review-agents.md +139 -5
- package/docs/coding-agent-setup.md +127 -2
- package/docs/coding-packages.md +105 -5
- package/docs/coding-services.md +3 -1
- package/docs/coding-worker-byo-images.md +239 -2
- package/docs/coding-worker-isolation.md +88 -23
- package/docs/getting-started-gke.md +225 -9
- package/docs/getting-started-identity-provider.md +21 -10
- package/docs/getting-started.md +140 -11
- package/docs/jira-agents.md +5 -1
- package/docs/knowledge.md +11 -0
- package/docs/native-sandbox.md +635 -0
- package/docs/release-verification.md +9 -0
- package/docs/security-deployment.md +113 -12
- package/docs/slack-notifications.md +252 -0
- package/docs/tool-secrets.md +252 -0
- package/docs/viewer-api.md +46 -5
- package/docs/viewer-api.openapi.yml +1404 -0
- package/help/admin-viewer.md +6 -2
- package/help/agent-recipes.md +27 -0
- package/help/architecture-agent.md +136 -0
- package/help/brokered-secrets.md +79 -0
- package/help/build-claude-code-image.md +131 -0
- package/help/build-worker-image.md +195 -0
- package/help/code-review-agents.md +68 -6
- package/help/coding-packages.md +31 -2
- package/help/creating-agents.md +14 -0
- package/help/deploy-gke.md +37 -6
- package/help/deployment-targets.md +24 -13
- package/help/errors/coding-provider-not-configured.md +2 -2
- package/help/errors/database-auth-invalid.md +28 -0
- package/help/errors/github-account-not-allowed.md +25 -0
- package/help/errors/low-power-disabled.md +29 -0
- package/help/errors/low-power-scale-forbidden.md +35 -0
- package/help/errors/low-power-wake-timeout.md +30 -0
- package/help/errors/mobile-disabled.md +31 -0
- package/help/errors/native-sandbox-capacity.md +22 -0
- package/help/errors/native-sandbox-deadline-exceeded.md +19 -0
- package/help/errors/native-sandbox-docker-failed.md +20 -0
- package/help/errors/native-sandbox-gateway-unavailable.md +21 -0
- package/help/errors/native-sandbox-image-not-pinned.md +19 -0
- package/help/errors/native-sandbox-isolation-mismatch.md +20 -0
- package/help/errors/native-sandbox-network-unenforced.md +22 -0
- package/help/errors/native-sandbox-requires-catalog.md +31 -0
- package/help/errors/native-sandbox-unavailable.md +19 -0
- package/help/errors/native-sandbox-warm-delivery-failed.md +21 -0
- package/help/errors/native-sandbox-worker-exited.md +20 -0
- package/help/errors/native-sandbox-worker-lost.md +20 -0
- package/help/errors/native-sandbox-worker-unready.md +22 -0
- package/help/errors/secret-broker-body-invalid.md +26 -0
- package/help/errors/secret-broker-changed.md +20 -0
- package/help/errors/secret-broker-config-invalid.md +30 -0
- package/help/errors/secret-broker-conflict.md +27 -0
- package/help/errors/secret-broker-destination-denied.md +28 -0
- package/help/errors/secret-broker-value-invalid.md +28 -0
- package/help/errors/secret-brokered.md +24 -0
- package/help/errors/secret-not-brokered.md +25 -0
- package/help/errors/slack-auth-failed.md +48 -0
- package/help/errors/slack-channel-unreachable.md +39 -0
- package/help/errors/slack-not-configured.md +33 -0
- package/help/getting-started.md +18 -0
- package/help/github.md +18 -1
- package/help/identity-and-access.md +5 -1
- package/help/local-repositories.md +35 -5
- package/help/low-power-mode.md +125 -0
- package/help/merge-order-check.md +117 -0
- package/help/mobile-app.md +54 -0
- package/help/native-sandbox.md +136 -0
- package/help/observability.md +8 -0
- package/help/related-pull-requests.md +70 -9
- package/help/repo-instructions-and-skills.md +91 -0
- package/help/review-fix-rounds.md +10 -0
- package/help/slack-notifications.md +62 -0
- package/help/troubleshooting/coding-workers.md +21 -0
- package/package.json +13 -5
- package/prisma/migrations/20261009000003_native_execution_mode/migration.sql +12 -0
- package/prisma/migrations/20261009000004_native_gateway_ledger/migration.sql +65 -0
- package/prisma/migrations/20261009000005_native_gateway_call_child_agent/migration.sql +5 -0
- package/prisma/migrations/20261009000006_run_deferred_start_stop/migration.sql +14 -0
- package/prisma/migrations/20261009000007_native_gateway_network_ready/migration.sql +5 -0
- package/prisma/migrations/20261010000001_workflow_notifications/migration.sql +99 -0
- package/prisma/migrations/20261011000001_native_gateway_session_status_index/migration.sql +2 -0
- package/prisma/migrations/20261012000001_native_warm_worker/migration.sql +32 -0
- package/prisma/migrations/20261013000001_coding_run_merge_order/migration.sql +2 -0
- package/prisma/migrations/20261013000001_secret_broker/migration.sql +17 -0
- package/prisma/migrations/20261013000002_deferred_review_request/migration.sql +4 -0
- package/prisma/migrations/20261014000000_repo_skills/migration.sql +15 -0
- package/prisma/migrations/20261015000001_coding_run_merge_order_index/migration.sql +2 -0
- package/prisma/migrations/20261015000002_no_change_rereview/migration.sql +2 -0
- package/prisma/schema.prisma +362 -91
|
@@ -7,6 +7,9 @@ wardby's published **driver base image** and point your agent's
|
|
|
7
7
|
`workerImageRef` at it, instead of waiting for wardby to hand-author a new
|
|
8
8
|
`Dockerfile.<toolchain>`.
|
|
9
9
|
|
|
10
|
+
The driver image is for Codex agents. A Claude Code agent's image starts from a
|
|
11
|
+
different base; see [Codex and Claude Code](#codex-and-claude-code).
|
|
12
|
+
|
|
10
13
|
## The driver image
|
|
11
14
|
|
|
12
15
|
`ghcr.io/wardby/wardby/wardby-coding-worker-driver` contains wardby's
|
|
@@ -14,7 +17,13 @@ compiled Node.js coding-worker driver, `git`, `ca-certificates`, and the
|
|
|
14
17
|
`wardby` user (uid/gid 10001) — nothing language-specific. It's built from
|
|
15
18
|
`src/coding-worker/Dockerfile.driver` and published on `driver-vN` git tags;
|
|
16
19
|
each release's GitHub Release notes carry the resolved
|
|
17
|
-
`@sha256:...` digest to pin.
|
|
20
|
+
`@sha256:...` digest to pin. `wardby doctor` prints the digest your installed
|
|
21
|
+
version's own workers are built on ("Base image for your own worker images:
|
|
22
|
+
…"); build on that one so your image matches the run input your Wardby sends.
|
|
23
|
+
|
|
24
|
+
An MCP assistant connected to Wardby can do the whole procedure for you: the
|
|
25
|
+
`build-worker-image` help article walks it through finding the toolchain,
|
|
26
|
+
writing and checking the Dockerfile, and setting `workerImageRef`.
|
|
18
27
|
|
|
19
28
|
The image deliberately stops before setting `USER`, `WORKDIR`, or
|
|
20
29
|
`ENTRYPOINT`, and before any of the hardened binary-absence checks wardby's
|
|
@@ -54,7 +63,38 @@ own READMEs tell it to — reports a failed command even when the suite is
|
|
|
54
63
|
green. `Dockerfile.node-python` symlinks `python` to `python3` for exactly
|
|
55
64
|
that reason, and asserts both work.
|
|
56
65
|
|
|
57
|
-
|
|
66
|
+
### Design for the run's filesystem
|
|
67
|
+
|
|
68
|
+
A run's root filesystem is read-only; `/tmp` and `/home/wardby` are empty
|
|
69
|
+
`noexec` tmpfs mounts of 16 to 64 MB (anything the image put there is hidden);
|
|
70
|
+
`/workspace` (the checkout, `CODING_DISK_MB` large, or the agent's
|
|
71
|
+
`codingProfile.workspaceDiskMb`) is the only place a run can write and execute;
|
|
72
|
+
and a run reaches no package registry except Wardby's npm and PyPI proxy. So:
|
|
73
|
+
|
|
74
|
+
- point every cache, build output and temp directory under `/workspace/.cache/`
|
|
75
|
+
(a `.cache` folder is never collected into the result, at any depth), and
|
|
76
|
+
create those directories from a small wrapper around the toolchain command,
|
|
77
|
+
keeping the `ENTRYPOINT` unchanged. For Go: `GOCACHE`, `GOTMPDIR` (`go test`
|
|
78
|
+
runs its test binary from there) and `GOMODCACHE`;
|
|
79
|
+
- bake dependencies into a read-only location the toolchain only reads from
|
|
80
|
+
(Go: a file-based module proxy filled by `go mod download`, used through
|
|
81
|
+
`GOPROXY=file://...`); a baked cache cannot be written to during a run;
|
|
82
|
+
- add build folders that stay in the checkout (Maven `target`, Gradle `build`)
|
|
83
|
+
to `codingProfile.collectExclude`.
|
|
84
|
+
|
|
85
|
+
Before using the image, run the project's tests against a throwaway clone with
|
|
86
|
+
a run's restrictions: `--read-only`, `--tmpfs /tmp:rw,noexec,nosuid,size=64m`,
|
|
87
|
+
`--tmpfs /home/wardby:rw,noexec,nosuid,size=64m,uid=10001,gid=10001`,
|
|
88
|
+
`--network none`, `--user 10001:10001`, `--cap-drop ALL` and
|
|
89
|
+
`--security-opt no-new-privileges`. The `build-worker-image` help article has a
|
|
90
|
+
complete Go Dockerfile that passes this check, recipes for Rust and Java, and
|
|
91
|
+
the exact command.
|
|
92
|
+
|
|
93
|
+
### Build and pin it
|
|
94
|
+
|
|
95
|
+
On a local quickstart install with the Docker launcher, `workerImageRef` can be
|
|
96
|
+
the local image ID (`docker image inspect --format '{{.Id}}' <your-tag>`).
|
|
97
|
+
Otherwise, build it, push it to your own registry, and note the resulting digest —
|
|
58
98
|
`docker inspect --format '{{index .RepoDigests 0}}' <your-tag>` after a push,
|
|
59
99
|
or read it straight from `docker buildx build --push`'s output.
|
|
60
100
|
|
|
@@ -75,6 +115,189 @@ point one at an arbitrary image without the step-up scope. The scope alone
|
|
|
75
115
|
isn't enough: the caller must also hold the admin role (see
|
|
76
116
|
[roles and privileged operations](security-deployment.md#roles-and-privileged-operations)).
|
|
77
117
|
|
|
118
|
+
## Codex and Claude Code
|
|
119
|
+
|
|
120
|
+
A Codex agent runs in one container, and its commands run there too. A Claude
|
|
121
|
+
Code agent uses two containers: one runs Claude Code and holds its API key; the
|
|
122
|
+
other runs the agent's commands and has your repository. `workerImageRef` always
|
|
123
|
+
replaces the container that runs commands.
|
|
124
|
+
|
|
125
|
+
| | Codex agent | Claude Code agent |
|
|
126
|
+
| ------------------------------ | ----------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------- |
|
|
127
|
+
| What `workerImageRef` replaces | the agent's only container | the container that runs commands |
|
|
128
|
+
| Build your image `FROM` | [the coding-worker driver image](#the-driver-image) (`driver-vN`) | the `wardby-claude-tool-runner` image from the same wardby release (or `wardby-claude-tool-runner-node-python`) |
|
|
129
|
+
| Rebuild when | the driver image changes | you upgrade wardby |
|
|
130
|
+
| Claude Code itself runs on | — | wardby's standard image, never yours |
|
|
131
|
+
|
|
132
|
+
The driver image sections above are for Codex. The subsections below are for
|
|
133
|
+
Claude Code. In them, "tool runner" means the container that runs a Claude Code
|
|
134
|
+
agent's commands.
|
|
135
|
+
|
|
136
|
+
### Build a custom image for a Claude Code agent
|
|
137
|
+
|
|
138
|
+
1. **Find your release's tool runner image.** It is the value of
|
|
139
|
+
`CODING_CLAUDE_TOOL_RUNNER_IMAGE` on your server. To start from Node and
|
|
140
|
+
Python, use `CODING_CLAUDE_TOOL_RUNNER_IMAGE_NODE_PYTHON_3_12` instead. A
|
|
141
|
+
release value looks like
|
|
142
|
+
`ghcr.io/wardby/wardby/wardby-claude-tool-runner@sha256:<digest>`. If the
|
|
143
|
+
value is a local image ID (`sha256:...` only), give it a name first, because
|
|
144
|
+
Docker cannot build `FROM` a bare ID:
|
|
145
|
+
`docker tag sha256:<id> wardby-tool-runner-base:local`.
|
|
146
|
+
2. **Make a build folder.** Use a small folder of your own, outside your
|
|
147
|
+
repository. Put in it the Dockerfile and only the files the Dockerfile
|
|
148
|
+
copies.
|
|
149
|
+
3. **Write the Dockerfile.** Start from the image in step 1, install your tools
|
|
150
|
+
as `root`, then switch back to the tool runner's user:
|
|
151
|
+
|
|
152
|
+
```dockerfile
|
|
153
|
+
FROM <image from step 1>
|
|
154
|
+
USER root
|
|
155
|
+
RUN apt-get update \
|
|
156
|
+
&& apt-get install -y --no-install-recommends <your packages> \
|
|
157
|
+
&& rm -rf /var/lib/apt/lists/*
|
|
158
|
+
USER 10001:10001
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
Keep these rules:
|
|
162
|
+
- The last `USER` line is `USER 10001:10001`.
|
|
163
|
+
- Do not set `ENTRYPOINT` or `CMD`. The tool runner starts with the base
|
|
164
|
+
image's own entrypoint.
|
|
165
|
+
- Install everything at build time. A run cannot install system packages.
|
|
166
|
+
- Put tools in `/usr/local/bin` or `/usr/bin`. Do not put anything under
|
|
167
|
+
`/tmp` or `/home/wardby`: a run hides both.
|
|
168
|
+
- Put environment settings your commands need in a file under
|
|
169
|
+
`/etc/profile.d/` (see "How the tool runner runs commands" below). `ENV`
|
|
170
|
+
lines in the Dockerfile do not reach the agent's commands.
|
|
171
|
+
|
|
172
|
+
4. **Build it:** `docker build --tag <name> <build folder>`. Release images are
|
|
173
|
+
`linux/amd64`. On an ARM machine, add `--platform linux/amd64` here and to
|
|
174
|
+
`docker run` in step 5.
|
|
175
|
+
5. **Run the tests the way a run would.** Clone the repository to a throwaway
|
|
176
|
+
folder (on Linux, make it writable: `chmod -R a+rwX <clone>`). Then run the
|
|
177
|
+
project's test command with a run's restrictions and its fixed environment,
|
|
178
|
+
so `ENV` lines in the image do not apply:
|
|
179
|
+
|
|
180
|
+
```sh
|
|
181
|
+
docker run --rm --read-only --tmpfs /tmp:rw,noexec,nosuid,size=64m --tmpfs /home/wardby:rw,noexec,nosuid,size=64m,uid=10001,gid=10001,mode=0700 --network none --user 10001:10001 --cap-drop ALL --security-opt no-new-privileges -v <clone>:/workspace -w /workspace --entrypoint env <name> -i HOME=/home/wardby LANG=C.UTF-8 PATH=/opt/wardby/bin:/usr/local/bin:/usr/bin:/bin sh -lc '<test command>'
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
On an ARM machine, add `--platform linux/amd64` after `docker run`. Fix the
|
|
185
|
+
Dockerfile until the tests pass, then delete the clone.
|
|
186
|
+
|
|
187
|
+
6. **Get an immutable reference.** A tag is refused.
|
|
188
|
+
- **Docker launcher** (wardby runs coding jobs with Docker on a single
|
|
189
|
+
machine): use the local image ID,
|
|
190
|
+
`docker image inspect --format '{{.Id}}' <name>`. Build on that machine.
|
|
191
|
+
Wardby never pulls these images.
|
|
192
|
+
- **Kubernetes:** push the image to a registry your cluster can pull from,
|
|
193
|
+
and use `<registry>/<name>@sha256:<digest>`.
|
|
194
|
+
7. **Point the agent at it.** Call `update_agent` with the agent's id. This
|
|
195
|
+
needs the `agents:admin` scope and the admin role:
|
|
196
|
+
|
|
197
|
+
```json
|
|
198
|
+
{
|
|
199
|
+
"id": "<agent id>",
|
|
200
|
+
"codingProfile": { "workerImageRef": "<reference from step 6>" }
|
|
201
|
+
}
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
8. **Try it.** Trigger a small task that uses your tools, such as "Run the
|
|
205
|
+
project's tests and report the results. Change nothing."
|
|
206
|
+
|
|
207
|
+
To undo it, set `codingProfile.workerImageRef` to `null`. The agent then uses
|
|
208
|
+
the standard tool runner for its `toolchain`.
|
|
209
|
+
|
|
210
|
+
### Languages other than Node and Python
|
|
211
|
+
|
|
212
|
+
A run can download packages only from wardby's package registry, which serves
|
|
213
|
+
npm and PyPI. Packages for any other language must be in the image. If a
|
|
214
|
+
package compiles native code, compile it in an earlier build stage and copy
|
|
215
|
+
only the results into the final image. The final image then needs no compiler.
|
|
216
|
+
|
|
217
|
+
The tool runner is based on Debian 12 (bookworm). Build stages that compile
|
|
218
|
+
code must use a bookworm-based image too, so the results work with the tool
|
|
219
|
+
runner's system libraries.
|
|
220
|
+
|
|
221
|
+
This example adds Ruby and a project's gems. It is a starting point to adapt,
|
|
222
|
+
not a tested recipe. Copy the project's `Gemfile` and `Gemfile.lock` into the
|
|
223
|
+
build folder first.
|
|
224
|
+
|
|
225
|
+
```dockerfile
|
|
226
|
+
# Stage 1: install Ruby and the project's gems, compiling any native code.
|
|
227
|
+
FROM ruby:3.3-slim-bookworm AS builder
|
|
228
|
+
RUN apt-get update \
|
|
229
|
+
&& apt-get install -y --no-install-recommends build-essential \
|
|
230
|
+
&& rm -rf /var/lib/apt/lists/*
|
|
231
|
+
ENV BUNDLE_PATH=/usr/local/bundle \
|
|
232
|
+
BUNDLE_DEPLOYMENT=true
|
|
233
|
+
WORKDIR /build
|
|
234
|
+
COPY Gemfile Gemfile.lock ./
|
|
235
|
+
RUN bundle install
|
|
236
|
+
|
|
237
|
+
# Stage 2: the tool runner from your wardby release, plus Ruby and the gems.
|
|
238
|
+
FROM <tool runner image from step 1>
|
|
239
|
+
USER root
|
|
240
|
+
# Shared libraries Ruby needs at run time, plus any library your native gems
|
|
241
|
+
# link to (for example libpq5 for pg).
|
|
242
|
+
RUN apt-get update \
|
|
243
|
+
&& apt-get install -y --no-install-recommends libffi8 libgmp10 libssl3 libyaml-0-2 zlib1g \
|
|
244
|
+
&& rm -rf /var/lib/apt/lists/*
|
|
245
|
+
# Ruby lives in /usr/local, and the gems in /usr/local/bundle.
|
|
246
|
+
COPY --from=builder /usr/local /usr/local
|
|
247
|
+
# Settings for the agent's commands (Dockerfile ENV lines do not reach them).
|
|
248
|
+
RUN ldconfig \
|
|
249
|
+
&& printf '%s\n' \
|
|
250
|
+
'export GEM_HOME=/usr/local/bundle' \
|
|
251
|
+
'export BUNDLE_PATH=/usr/local/bundle' \
|
|
252
|
+
'export BUNDLE_APP_CONFIG=/usr/local/bundle' \
|
|
253
|
+
'export BUNDLE_DEPLOYMENT=true' \
|
|
254
|
+
> /etc/profile.d/ruby.sh \
|
|
255
|
+
&& sh -lc 'ruby --version && bundle --version'
|
|
256
|
+
USER 10001:10001
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
Pin the `ruby` image by digest for repeatable builds. Rebuild the image when
|
|
260
|
+
the project's `Gemfile.lock` changes.
|
|
261
|
+
|
|
262
|
+
### If a run fails
|
|
263
|
+
|
|
264
|
+
| What you see | What it usually means |
|
|
265
|
+
| --------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
266
|
+
| `worker_tool_runner_unreachable` | The image was not built `FROM` the tool runner of the same wardby release, or the Dockerfile changed its `USER` or `ENTRYPOINT`. |
|
|
267
|
+
| A command is "not found" | The tool is not in the image, or a setting it needs is in an `ENV` line instead of `/etc/profile.d/`. |
|
|
268
|
+
| "Permission denied" when running a file | On Docker, something writes a program to `/tmp` or `/home/wardby` and runs it. Point that tool's temporary or cache folder under `/workspace/.cache/`. |
|
|
269
|
+
|
|
270
|
+
### How the tool runner runs commands
|
|
271
|
+
|
|
272
|
+
These details explain the rules above.
|
|
273
|
+
|
|
274
|
+
- **User and privileges.** Commands always run as user `10001`, with no Linux
|
|
275
|
+
capabilities and `no-new-privileges`. `root` and setuid programs do not work
|
|
276
|
+
at run time.
|
|
277
|
+
- **Filesystem.** The root filesystem is read-only. `/tmp` and `/home/wardby`
|
|
278
|
+
are small, empty scratch space; anything the image put there is hidden. On
|
|
279
|
+
Docker, files there cannot be run. `/workspace` holds the repository; put
|
|
280
|
+
caches, build output and temporary files under `/workspace/.cache/`.
|
|
281
|
+
- **Shell and environment.** Each command runs as `sh -lc "<command>"` with a
|
|
282
|
+
fixed environment: `HOME=/home/wardby`, `LANG=C.UTF-8`, `TMPDIR` under
|
|
283
|
+
`/workspace/.cache/`, wardby's package registry settings, and
|
|
284
|
+
`PATH=/opt/wardby/bin:/usr/local/bin:/usr/bin:/bin`. The login shell reads
|
|
285
|
+
`/etc/profile` and the files in `/etc/profile.d/`, so that is where your own
|
|
286
|
+
settings go. `/etc/profile` resets `PATH` to Debian's default, so add any
|
|
287
|
+
other folder to `PATH` in your `/etc/profile.d/` file.
|
|
288
|
+
- **Network.** The tool runner reaches only wardby's package registry (npm and
|
|
289
|
+
PyPI).
|
|
290
|
+
- **Images on the Docker launcher are never pulled.** When wardby runs coding
|
|
291
|
+
jobs with Docker on a single machine, the image must already be on that
|
|
292
|
+
machine. Kubernetes needs a registry digest
|
|
293
|
+
(`repo@sha256:<64 hex>`).
|
|
294
|
+
- **Server settings.** Claude Code itself always runs on
|
|
295
|
+
`CODING_CLAUDE_WORKER_IMAGE`. The server still needs both
|
|
296
|
+
`CODING_CLAUDE_WORKER_IMAGE` and `CODING_CLAUDE_TOOL_RUNNER_IMAGE` set, even
|
|
297
|
+
for agents with their own `workerImageRef`.
|
|
298
|
+
- **Toolchain.** While `workerImageRef` is set, `toolchain` and
|
|
299
|
+
`toolchainVersion` do not choose the image, for either builder.
|
|
300
|
+
|
|
78
301
|
## Running services with a BYO image
|
|
79
302
|
|
|
80
303
|
An agent whose `codingProfile.services` allows a service ([coding
|
|
@@ -85,6 +308,20 @@ a worker built on an older driver base doesn't recognize the input's
|
|
|
85
308
|
without them. Rebuild your image on a current `driver-vN` digest (see "The
|
|
86
309
|
driver image" above) before allowing any service on an agent that uses it.
|
|
87
310
|
|
|
311
|
+
## Repository skills need a current driver
|
|
312
|
+
|
|
313
|
+
On a Codex agent, a `workerImageRef` image never receives Claude Code's
|
|
314
|
+
repository context (`claudeContext`) or `claudeBareMode`. The one new input key
|
|
315
|
+
it can see is `repoSkills`, which wardby writes only when an agent sets
|
|
316
|
+
`repoSkills: false`. A worker built on
|
|
317
|
+
an older driver base validates its input with a strict schema, so it rejects
|
|
318
|
+
runs of agents with `repoSkills: false` (the run fails with
|
|
319
|
+
`worker_input_failed`), and it keeps offering Codex's built-in skills
|
|
320
|
+
(`imagegen`, `openai-docs`, `skill-creator`, `skill-installer`), which a
|
|
321
|
+
current driver always disables. Rebuild any `workerImageRef` image on a
|
|
322
|
+
current `driver-vN` digest (see "The driver image" above) before relying on
|
|
323
|
+
either behavior.
|
|
324
|
+
|
|
88
325
|
## What this doesn't cover
|
|
89
326
|
|
|
90
327
|
wardby does not re-validate the contents of your built image beyond the
|
|
@@ -1,16 +1,24 @@
|
|
|
1
1
|
# Coding Worker Isolation
|
|
2
2
|
|
|
3
|
-
Wardby
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
Wardby runs Codex and Claude Code coding workers through Docker or Kubernetes
|
|
4
|
+
launchers. Both fail closed when the effective runtime does not match the
|
|
5
|
+
reviewed policy. Claude Code separates its model-facing worker from its
|
|
6
|
+
workspace-facing tool runner: Docker uses companion containers, while
|
|
7
|
+
Kubernetes uses a native sidecar in the run pod.
|
|
6
8
|
|
|
7
9
|
## Security Boundary
|
|
8
10
|
|
|
9
|
-
Coding-agent repositories and instructions are untrusted. The
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
11
|
+
Coding-agent repositories and instructions are untrusted. The Wardby control
|
|
12
|
+
plane, immutable worker images, dedicated coding proxy, and the platform
|
|
13
|
+
enforcing container and network policies are trusted.
|
|
14
|
+
|
|
15
|
+
### Docker launcher
|
|
16
|
+
|
|
17
|
+
The Docker daemon and host kernel are trusted. Docker containers are defense
|
|
18
|
+
in depth rather than a VM boundary. Production should run the Docker host on a
|
|
19
|
+
dedicated worker node or VM with no production credentials beyond those
|
|
20
|
+
required by the proxy. The Kubernetes launcher section below describes the
|
|
21
|
+
corresponding pod and network rules.
|
|
14
22
|
|
|
15
23
|
The Codex worker has one network attachment: a unique per-run internal bridge using
|
|
16
24
|
Docker's isolated gateway mode. It has no default external route, published
|
|
@@ -588,6 +596,7 @@ Kubernetes-specific settings (`src/config/providers.ts`,
|
|
|
588
596
|
| `KUBERNETES_CONTEXT` | (unset → in-cluster/default kubeconfig context) | Which kubeconfig context `ClientNodeKubernetesApi` connects with. |
|
|
589
597
|
| `KUBERNETES_RUNTIME_CLASS` | (unset) | e.g. `gvisor` on GKE. Unset means pods run without a sandboxing runtime class — logged once per launch as `kubernetes_runtime_class_unset` and development-only. |
|
|
590
598
|
| `KUBERNETES_RESOURCE_QUOTA` | (unset) | Name of the namespace ResourceQuota run pods count against, e.g. `wardby-coding` in the GKE overlay. When set, a run whose pod would not fit the quota's free room waits in the coding queue instead of failing at pod create. Needs `get` on that one `resourcequotas` object. Unset means no check. |
|
|
599
|
+
| `KUBERNETES_PLATFORM` | `generic` | `generic`, `gke-autopilot` or `eks`. Selects the cluster's admission rules that run pods are conformed to. `gke-autopilot` and `eks` both require `KUBERNETES_RUNTIME_CLASS=gvisor`; see the platform profile section below. |
|
|
591
600
|
| `KUBERNETES_RUN_PRIORITY_CLASS` | (unset) | PriorityClass for run pods and the preflight canary, e.g. `wardby-coding-run` in the GKE overlay. Must be an existing class and a DNS-1123 subdomain; `system-` classes are refused. Unset means no class (priority 0). |
|
|
592
601
|
|
|
593
602
|
The `CODING_CPUS` / `CODING_MEMORY_MB` / `CODING_PIDS` / `CODING_DISK_MB` /
|
|
@@ -798,6 +807,16 @@ landed on — cannot appear in it. That case reached a live Autopilot launch wit
|
|
|
798
807
|
every dry-run-derived check passing, and is now pinned by tests. Treat a
|
|
799
808
|
captured fixture as a lower bound on what a platform mutates.
|
|
800
809
|
|
|
810
|
+
The `eks` profile is for Amazon EKS clusters with a dedicated gVisor node group.
|
|
811
|
+
EKS has no managed sandbox runtime, so the cluster must provide a RuntimeClass
|
|
812
|
+
named `gvisor` with handler `runsc` whose `scheduling` block selects nodes
|
|
813
|
+
labelled `wardby.dev/sandbox=gvisor` and tolerates the taint
|
|
814
|
+
`sandbox=gvisor:NoSchedule` on those nodes. Kubernetes' RuntimeClass admission
|
|
815
|
+
merges that block into each pod; Wardby sets neither the node selector nor the
|
|
816
|
+
toleration itself, and the `eks` profile forgives exactly that nodeSelector and
|
|
817
|
+
that toleration and nothing else. Pod resources follow the generic rules, plus
|
|
818
|
+
an explicit ephemeral-storage request and limit.
|
|
819
|
+
|
|
801
820
|
### The enforcement gate
|
|
802
821
|
|
|
803
822
|
The Kubernetes API can create a NetworkPolicy object without that policy
|
|
@@ -808,12 +827,12 @@ not just the harness: the worker gate could otherwise
|
|
|
808
827
|
open on a pod whose isolation isn't active yet.
|
|
809
828
|
|
|
810
829
|
The fix, before seeding or opening the worker gate: the launcher execs into
|
|
811
|
-
the keeper (which shares the pod's network namespace with the worker)
|
|
812
|
-
`node -e` probe
|
|
813
|
-
proxy Service's ClusterIP, in the same pass** — `8787` (the proxy
|
|
814
|
-
the run policy permits) and `8788` (the deny port, which no run
|
|
815
|
-
permits). Only the outcome **(8787 connected, 8788 blocked)** counts
|
|
816
|
-
streak.
|
|
830
|
+
the keeper (which shares the pod's network namespace with the worker) a
|
|
831
|
+
`node -e` script whose every probe measures **both of the coding proxy's ports
|
|
832
|
+
against the proxy Service's ClusterIP, in the same pass** — `8787` (the proxy
|
|
833
|
+
itself, which the run policy permits) and `8788` (the deny port, which no run
|
|
834
|
+
policy ever permits). Only the outcome **(8787 connected, 8788 blocked)** counts
|
|
835
|
+
toward the streak.
|
|
817
836
|
|
|
818
837
|
Stated exactly, that outcome proves: **the SYN to 8788 was dropped somewhere on
|
|
819
838
|
the path, while the same destination answered on 8787.** That the drop was the
|
|
@@ -861,10 +880,21 @@ from "unserved" is not a witness — and such a cluster needs a different one.
|
|
|
861
880
|
|
|
862
881
|
It requires **3 consecutive proven results, 500ms apart** (anything else
|
|
863
882
|
resets the streak — this guards against a single dropped SYN packet on an
|
|
864
|
-
allowed path being misread as "policy enforced")
|
|
865
|
-
|
|
883
|
+
allowed path being misread as "policy enforced"). The whole streak runs in a
|
|
884
|
+
single keeper exec: the script exits successfully only after three consecutive
|
|
885
|
+
proven probes, and stops at the first probe that is not proven, exiting with
|
|
886
|
+
that probe's result. A broken streak is retried from zero 500ms later, bounded
|
|
887
|
+
by `enforcementTimeoutMs` (default 30,000ms — configurable via
|
|
866
888
|
`KubernetesJobLauncherOptions.enforcementTimeoutMs`; a drop-style CNI can
|
|
867
|
-
need close to this whole window).
|
|
889
|
+
need close to this whole window). Each probe has a time budget of
|
|
890
|
+
`KUBERNETES_ENFORCEMENT_EXEC_TIMEOUT_MS` (default 10,000ms), so one streak exec
|
|
891
|
+
is allowed three budgets plus the two 500ms gaps (31,000ms at the default), and
|
|
892
|
+
the launcher never lets `enforcementTimeoutMs` fall below that one-streak value
|
|
893
|
+
— at the defaults the effective bound is therefore 31,000ms. The bound is
|
|
894
|
+
checked between streak execs, so a launch can run past it by up to one streak
|
|
895
|
+
exec. An exec that exceeds its timeout fails the launch with
|
|
896
|
+
`kubernetes_exec_timeout`. The
|
|
897
|
+
verdict at the bound comes from the
|
|
868
898
|
_last_ probe — not from whether any probe was ever unavailable, so an early
|
|
869
899
|
blip while the pod's networking came up does not misdirect the operator — and
|
|
870
900
|
the three non-proven outcomes stay distinct, because each sends an operator
|
|
@@ -900,9 +930,9 @@ default 90,000ms):
|
|
|
900
930
|
1. `platform` — pure configuration, checked before any cluster API call:
|
|
901
931
|
`assertPlatformConfig` (`src/providers/jobs/kubernetes-platform.ts`) refuses
|
|
902
932
|
a deployment that cannot work under `KUBERNETES_PLATFORM`. Under
|
|
903
|
-
`gke-autopilot`
|
|
904
|
-
|
|
905
|
-
|
|
933
|
+
`gke-autopilot` and `eks`, `KUBERNETES_RUNTIME_CLASS` must be `gvisor` (an
|
|
934
|
+
unset or different runtime class is refused, not warned about). Under
|
|
935
|
+
`gke-autopilot` the effective `CODING_MAX_DISK_MB` must also leave room
|
|
906
936
|
for the worker container's 1 GiB reservation inside Autopilot's 10 GiB pod
|
|
907
937
|
ephemeral-storage ceiling. The same assertion runs again at process
|
|
908
938
|
start-up in `buildConfiguredExecutor`
|
|
@@ -931,20 +961,55 @@ default 90,000ms):
|
|
|
931
961
|
5. `canary` — creates a real run pod + NetworkPolicy from the same builders
|
|
932
962
|
as a live run, running a script that waits for policy enforcement (as
|
|
933
963
|
above) then attempts DNS resolution, a connect to the proxy's deny port,
|
|
934
|
-
the internet (`1.1.1.1:443`), the metadata
|
|
935
|
-
|
|
964
|
+
the internet (`1.1.1.1:443`), the cloud metadata endpoints (`169.254.169.254:80`,
|
|
965
|
+
the EKS Pod Identity agent `169.254.170.23:80` and its IPv6 address
|
|
966
|
+
`[fd00:ec2::23]:80`, and EC2's IPv6 metadata address `[fd00:ec2::254]:80`), and the proxy itself —
|
|
967
|
+
requiring every one of the first four kinds to fail and the proxy connect to
|
|
936
968
|
succeed. Any other outcome, or a canary pod that itself fails to
|
|
937
969
|
schedule/run, is `kubernetes_isolation_unsupported:canary`.
|
|
938
970
|
|
|
939
971
|
This whole preflight is **memoized per launcher instance and its failure is
|
|
940
972
|
sticky**: `KubernetesJobLauncher.runPreflight()` caches the first call's
|
|
941
|
-
promise (`this.preflightResult ??= ...`, `kubernetes.ts:
|
|
973
|
+
promise (`this.preflightResult ??= ...`, `kubernetes.ts:790`), including
|
|
942
974
|
a rejection — so once a launcher process has seen preflight fail, every
|
|
943
975
|
subsequent `launch()` in that process fails immediately with the same error
|
|
944
976
|
without re-probing the cluster. A fresh preflight requires a new process
|
|
945
977
|
(or, from the CLI, a fresh `wardby coding preflight` invocation, which is
|
|
946
978
|
not memoized).
|
|
947
979
|
|
|
980
|
+
**A long-running server process starts this same memoized preflight at
|
|
981
|
+
start-up, not on the first coding run.** `wardby serve`, `wardby mcp` (both
|
|
982
|
+
transports), and `wardby scheduler` each call `KubernetesJobLauncher.warmUp()`
|
|
983
|
+
right after the executor is built, fire-and-forget: it runs the identical
|
|
984
|
+
preflight `launch()` would otherwise run lazily, so the first coding run
|
|
985
|
+
after a restart doesn't pay the preflight's own cost (a canary pod, routinely
|
|
986
|
+
tens of seconds on a resource-constrained cluster such as `kind`).
|
|
987
|
+
`wardby scheduler` needs this just as much as the other two — it dispatches
|
|
988
|
+
scheduled coding runs through its own Kubernetes executor without ever
|
|
989
|
+
starting an MCP server, so without this it would still pay the lazy cost on
|
|
990
|
+
its first scheduled run. The result is logged once at start-up — an info
|
|
991
|
+
line on success, a warning naming the failure code (e.g.
|
|
992
|
+
`kubernetes_isolation_unsupported:<check>`) otherwise.
|
|
993
|
+
|
|
994
|
+
**A start-up warm-up's failure is logged and retried on the next run, not
|
|
995
|
+
left stuck until restart.** This is the one way a preflight failure is
|
|
996
|
+
_not_ memoized: a transient cluster problem at process start (the API
|
|
997
|
+
server briefly unreachable, a slow CNI not yet programmed, and the like)
|
|
998
|
+
must not fail every coding run for the rest of that process's life, so a
|
|
999
|
+
failed warm-up clears its own failed attempt once it has logged it, and the
|
|
1000
|
+
_next_ `launch()` runs the preflight fresh — succeeding if the cluster has
|
|
1001
|
+
since recovered. A `launch()` that was already waiting on that same
|
|
1002
|
+
in-flight warm-up attempt still fails with that attempt's error (it shares
|
|
1003
|
+
the same preflight call), exactly as it always has; it's only the attempt
|
|
1004
|
+
_after_ that one which retries. A preflight failure `launch()` triggers
|
|
1005
|
+
itself — because no warm-up ran, or because a warm-up's cleared failure was
|
|
1006
|
+
never retried before the next `launch()` found the cluster still broken —
|
|
1007
|
+
keeps the original behavior exactly: it stays memoized, failing every
|
|
1008
|
+
subsequent `launch()` in that process until it is restarted. This never
|
|
1009
|
+
runs for a one-shot CLI command (`wardby run`, `wardby coding preflight`,
|
|
1010
|
+
migrations, imports) — only for a process that stays up to serve or
|
|
1011
|
+
dispatch runs.
|
|
1012
|
+
|
|
948
1013
|
**A hung pod create during preflight can leave a preflight pod and its
|
|
949
1014
|
NetworkPolicy behind.** If `createPod` never settles (rather than failing),
|
|
950
1015
|
the preflight's own timeout still fires and the caller sees `:timeout`, but
|