@wardby/cli 0.2.0 → 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 +183 -71
- package/dist/claude-coding-worker/driver.d.ts +7 -1
- package/dist/claude-coding-worker/driver.js +31 -2
- package/dist/claude-coding-worker/main.js +5 -2
- 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 +99 -3
- package/dist/coding/protocol.js +155 -4
- 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 +18 -0
- package/dist/coding-worker/artifact.js +48 -6
- 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 +14 -1
- package/dist/coding-worker/driver.js +106 -14
- package/dist/coding-worker/errors.d.ts +7 -0
- package/dist/coding-worker/errors.js +26 -0
- package/dist/coding-worker/main.js +13 -4
- 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 +109 -6
- 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/coding-queue.d.ts +1 -1
- package/dist/core/datastores.d.ts +1 -1
- package/dist/core/datastores.js +18 -2
- package/dist/core/db.d.ts +40 -2
- package/dist/core/db.js +75 -2
- package/dist/core/dispatch.d.ts +49 -7
- package/dist/core/dispatch.js +290 -45
- 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/http-runtime.js +3 -4
- package/dist/core/in-flight-runs.d.ts +8 -0
- package/dist/core/in-flight-runs.js +56 -0
- package/dist/core/lease.d.ts +1 -1
- package/dist/core/private-directory.d.ts +20 -0
- package/dist/core/private-directory.js +36 -0
- package/dist/core/provider-wording.d.ts +12 -0
- package/dist/core/provider-wording.js +44 -0
- package/dist/core/reconciler.d.ts +39 -3
- 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 +31 -9
- package/dist/core/runner.js +237 -40
- package/dist/core/scheduler.d.ts +1 -1
- package/dist/core/secrets.d.ts +12 -3
- package/dist/core/secrets.js +25 -6
- package/dist/core/subagent-memory-tools.d.ts +2 -2
- 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 +9 -2
- package/dist/core/webhooks.js +23 -2
- package/dist/generated/prisma/browser.d.ts +328 -0
- package/dist/generated/prisma/browser.js +17 -0
- package/dist/generated/prisma/client.d.ts +347 -0
- package/dist/generated/prisma/client.js +34 -0
- package/dist/generated/prisma/commonInputTypes.d.ts +839 -0
- package/dist/generated/prisma/commonInputTypes.js +10 -0
- package/dist/generated/prisma/enums.d.ts +59 -0
- package/dist/generated/prisma/enums.js +59 -0
- package/dist/generated/prisma/internal/class.d.ts +605 -0
- package/dist/generated/prisma/internal/class.js +49 -0
- package/dist/generated/prisma/internal/prismaNamespace.d.ts +4494 -0
- package/dist/generated/prisma/internal/prismaNamespace.js +628 -0
- package/dist/generated/prisma/internal/prismaNamespaceBrowser.d.ts +635 -0
- package/dist/generated/prisma/internal/prismaNamespaceBrowser.js +599 -0
- package/dist/generated/prisma/models/Agent.d.ts +3560 -0
- package/dist/generated/prisma/models/Agent.js +1 -0
- package/dist/generated/prisma/models/AgentDatastore.d.ts +1210 -0
- package/dist/generated/prisma/models/AgentDatastore.js +1 -0
- package/dist/generated/prisma/models/AgentMemory.d.ts +986 -0
- package/dist/generated/prisma/models/AgentMemory.js +1 -0
- package/dist/generated/prisma/models/AgentRepository.d.ts +1425 -0
- package/dist/generated/prisma/models/AgentRepository.js +1 -0
- package/dist/generated/prisma/models/AgentSecret.d.ts +1212 -0
- package/dist/generated/prisma/models/AgentSecret.js +1 -0
- package/dist/generated/prisma/models/AgentSubAgent.d.ts +1217 -0
- package/dist/generated/prisma/models/AgentSubAgent.js +1 -0
- package/dist/generated/prisma/models/AgentTool.d.ts +1403 -0
- package/dist/generated/prisma/models/AgentTool.js +1 -0
- package/dist/generated/prisma/models/AuthFormChallenge.d.ts +1298 -0
- package/dist/generated/prisma/models/AuthFormChallenge.js +1 -0
- package/dist/generated/prisma/models/AuthLoginKey.d.ts +1245 -0
- package/dist/generated/prisma/models/AuthLoginKey.js +1 -0
- package/dist/generated/prisma/models/AuthRateLimit.d.ts +979 -0
- package/dist/generated/prisma/models/AuthRateLimit.js +1 -0
- package/dist/generated/prisma/models/AuthSession.d.ts +1376 -0
- package/dist/generated/prisma/models/AuthSession.js +1 -0
- package/dist/generated/prisma/models/AuthUser.d.ts +1700 -0
- package/dist/generated/prisma/models/AuthUser.js +1 -0
- package/dist/generated/prisma/models/BudgetGroup.d.ts +1548 -0
- package/dist/generated/prisma/models/BudgetGroup.js +1 -0
- package/dist/generated/prisma/models/CodingAgentProfile.d.ts +1681 -0
- package/dist/generated/prisma/models/CodingAgentProfile.js +1 -0
- package/dist/generated/prisma/models/CodingProxyRequest.d.ts +1749 -0
- package/dist/generated/prisma/models/CodingProxyRequest.js +1 -0
- package/dist/generated/prisma/models/CodingProxySession.d.ts +1626 -0
- package/dist/generated/prisma/models/CodingProxySession.js +1 -0
- package/dist/generated/prisma/models/CodingRun.d.ts +3583 -0
- package/dist/generated/prisma/models/CodingRun.js +1 -0
- package/dist/generated/prisma/models/CodingService.d.ts +1348 -0
- package/dist/generated/prisma/models/CodingService.js +1 -0
- package/dist/generated/prisma/models/Datastore.d.ts +1434 -0
- package/dist/generated/prisma/models/Datastore.js +1 -0
- package/dist/generated/prisma/models/DatastoreEntry.d.ts +1314 -0
- package/dist/generated/prisma/models/DatastoreEntry.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/OAuthAuthorizationCode.d.ts +1526 -0
- package/dist/generated/prisma/models/OAuthAuthorizationCode.js +1 -0
- package/dist/generated/prisma/models/OAuthAuthorizationRequest.d.ts +1350 -0
- package/dist/generated/prisma/models/OAuthAuthorizationRequest.js +1 -0
- package/dist/generated/prisma/models/OAuthClient.d.ts +1289 -0
- package/dist/generated/prisma/models/OAuthClient.js +1 -0
- package/dist/generated/prisma/models/OAuthFamily.d.ts +1540 -0
- package/dist/generated/prisma/models/OAuthFamily.js +1 -0
- package/dist/generated/prisma/models/OAuthGrant.d.ts +1280 -0
- package/dist/generated/prisma/models/OAuthGrant.js +1 -0
- package/dist/generated/prisma/models/Principal.d.ts +2245 -0
- package/dist/generated/prisma/models/Principal.js +1 -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 +2669 -0
- package/dist/generated/prisma/models/Run.js +1 -0
- 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/SchedulerLease.d.ts +970 -0
- package/dist/generated/prisma/models/SchedulerLease.js +1 -0
- package/dist/generated/prisma/models/Secret.d.ts +1399 -0
- package/dist/generated/prisma/models/Secret.js +1 -0
- package/dist/generated/prisma/models/SecretElicitationOutcome.d.ts +973 -0
- package/dist/generated/prisma/models/SecretElicitationOutcome.js +1 -0
- package/dist/generated/prisma/models/Task.d.ts +1176 -0
- package/dist/generated/prisma/models/Task.js +1 -0
- package/dist/generated/prisma/models/Tool.d.ts +1490 -0
- package/dist/generated/prisma/models/Tool.js +1 -0
- package/dist/generated/prisma/models/Webhook.d.ts +1386 -0
- package/dist/generated/prisma/models/Webhook.js +1 -0
- package/dist/generated/prisma/models.d.ts +45 -0
- package/dist/generated/prisma/models.js +1 -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 +6 -1
- package/dist/import/create.js +68 -14
- package/dist/import/index.d.ts +1 -1
- 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 +37 -63
- package/dist/mcp/auth/ownership.js +19 -14
- package/dist/mcp/auth/principal.d.ts +1 -1
- 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 +32 -3
- package/dist/mcp/auth/resource-server.js +84 -3
- package/dist/mcp/auth/self-hosted/browser.js +22 -8
- package/dist/mcp/auth/self-hosted/cli.d.ts +1 -1
- package/dist/mcp/auth/self-hosted/cli.js +47 -13
- package/dist/mcp/auth/self-hosted/credentials.d.ts +21 -3
- package/dist/mcp/auth/self-hosted/credentials.js +62 -4
- package/dist/mcp/auth/self-hosted/rate-limit.d.ts +1 -1
- package/dist/mcp/auth/self-hosted/session.d.ts +3 -2
- package/dist/mcp/context.d.ts +28 -2
- 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.d.ts +1 -1
- package/dist/mcp/server.js +27 -9
- package/dist/mcp/shared/page-style.d.ts +16 -0
- package/dist/mcp/shared/page-style.js +129 -0
- package/dist/mcp/tasks/manager.d.ts +1 -1
- package/dist/mcp/tools/agents.js +447 -49
- package/dist/mcp/tools/budget-groups.js +3 -3
- package/dist/mcp/tools/datastore.js +23 -16
- 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 +4 -7
- package/dist/mcp/tools/secret-elicitation-form.d.ts +1 -1
- package/dist/mcp/tools/secret-elicitation-form.js +10 -28
- package/dist/mcp/tools/secret-elicitation.d.ts +1 -1
- 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 +53 -17
- package/dist/mcp/tools/tools.d.ts +41 -0
- package/dist/mcp/tools/tools.js +202 -41
- package/dist/mcp/tools/trigger.js +36 -6
- package/dist/mcp/tools/webhooks.js +15 -4
- package/dist/mcp/transport/streamable-http.d.ts +11 -1
- package/dist/mcp/transport/streamable-http.js +32 -2
- package/dist/mcp/unattended-schedules.d.ts +1 -1
- package/dist/mcp/webhooks/ingress.d.ts +1 -1
- package/dist/providers/auth/delegating.d.ts +19 -0
- package/dist/providers/auth/delegating.js +71 -0
- package/dist/providers/auth/index.d.ts +3 -1
- package/dist/providers/auth/index.js +11 -0
- package/dist/providers/auth/self-hosted.d.ts +4 -3
- 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 +18 -1
- 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 +18 -1
- 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/datastore/postgres.d.ts +1 -1
- package/dist/providers/engine/types.d.ts +10 -0
- package/dist/providers/executor/build.d.ts +3 -3
- package/dist/providers/executor/composition.d.ts +4 -1
- package/dist/providers/executor/composition.js +19 -0
- package/dist/providers/executor/container.d.ts +106 -4
- package/dist/providers/executor/container.js +305 -22
- package/dist/providers/executor/dbos.d.ts +3 -4
- 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 +60 -2
- package/dist/providers/jobs/docker.js +313 -50
- 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 +117 -31
- package/dist/providers/jobs/types.d.ts +20 -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/memory/postgres.d.ts +1 -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 +79 -14
- 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 +34 -32
- package/dist/quickstart/migrate.d.ts +23 -0
- package/dist/quickstart/migrate.js +125 -0
- 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 +30 -11
- package/prisma/migrate.config.mjs +20 -0
- 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 +417 -23
|
@@ -0,0 +1,657 @@
|
|
|
1
|
+
# Security deployment and recovery
|
|
2
|
+
|
|
3
|
+
The secured self-hosted HTTP implementation is protected by database, HTTP,
|
|
4
|
+
browser, migration, and review gates. `npm install @wardby/cli` installs
|
|
5
|
+
audit-clean: the published package no longer installs the Prisma CLI, and the
|
|
6
|
+
repository's development tree is clean by reviewed overrides. Deployment
|
|
7
|
+
controls in this guide still apply.
|
|
8
|
+
|
|
9
|
+
## Supported runtime and delegated operation
|
|
10
|
+
|
|
11
|
+
Use Node.js 24 or newer. Development pins Node 24 through `.nvmrc`, and the
|
|
12
|
+
production runtime image is built on a pinned Node 24 base image.
|
|
13
|
+
Stdio remains local and trusted, with `LOCAL_PRINCIPAL` as its owner identity.
|
|
14
|
+
It holds every scope and every role.
|
|
15
|
+
Delegated HTTP requires `AUTH_ISSUER`, `AUTH_JWKS_URI`, and `AUTH_AUDIENCE`.
|
|
16
|
+
`MCP_CANONICAL_URI` and `AUTH_AUDIENCE` must be identical normalized HTTPS URLs.
|
|
17
|
+
Only explicit loopback development may use HTTP.
|
|
18
|
+
Follow [Bring your own identity provider](getting-started-identity-provider.md)
|
|
19
|
+
for the complete audience, scope, client-registration, configuration, and
|
|
20
|
+
verification procedure.
|
|
21
|
+
|
|
22
|
+
Terminate TLS at the reverse proxy. Forward exactly the canonical Host,
|
|
23
|
+
including a non-default port, and block direct access to the application port.
|
|
24
|
+
The app does not trust Forwarded/X-Forwarded-* for identity, origins, or rate
|
|
25
|
+
limiting. Configure proxy rate/connection limits as well. Behind a proxy the
|
|
26
|
+
application's per-IP limiter sees the proxy's IP, so its limit is shared by
|
|
27
|
+
those clients; do not fix that by trusting arbitrary forwarded IP headers.
|
|
28
|
+
|
|
29
|
+
Browser origins default to the canonical origin. `MCP_ALLOWED_ORIGINS` accepts
|
|
30
|
+
comma-separated exact additional origins, without paths or wildcards. Login,
|
|
31
|
+
consent, and logout POSTs always require the canonical Origin, even when an
|
|
32
|
+
additional MCP browser origin is allowed. Tokens must have a nonblank subject
|
|
33
|
+
of at most 512 UTF-8 bytes. External subject spelling and case are preserved.
|
|
34
|
+
|
|
35
|
+
## Self-hosted provisioning
|
|
36
|
+
|
|
37
|
+
Generate three independent random 32-byte keys, each encoded as 64 hex
|
|
38
|
+
characters. `openssl rand -hex 32` generates one key; run it independently
|
|
39
|
+
for `AUTH_SIGNING_KEY`, `AUTH_CREDENTIAL_HASH_KEY`, and `SECRET_APP_KEY`.
|
|
40
|
+
Supply them through the deployment secret manager. Never put them in the
|
|
41
|
+
image, shell history, repository, issue comments, or logs. Existing signing
|
|
42
|
+
key strings are not automatically compatible with the new hex encoding.
|
|
43
|
+
|
|
44
|
+
Use the same database and credential hash key for provisioning and the server:
|
|
45
|
+
|
|
46
|
+
```sh
|
|
47
|
+
node dist/cli.js auth user create --subject user-identifier
|
|
48
|
+
node dist/cli.js auth user create --subject operator-identifier --role admin
|
|
49
|
+
node dist/cli.js auth user list
|
|
50
|
+
node dist/cli.js auth user grant --subject user-identifier --role package-approver
|
|
51
|
+
node dist/cli.js auth user grant --subject user-identifier --revoke-role package-approver
|
|
52
|
+
node dist/cli.js auth user grant --subject user-identifier --role service-manager
|
|
53
|
+
node dist/cli.js auth key create --subject user-identifier
|
|
54
|
+
node dist/cli.js auth key list --subject user-identifier
|
|
55
|
+
node dist/cli.js auth key revoke PUBLIC-KEY-ID
|
|
56
|
+
node dist/cli.js auth user disable --subject user-identifier
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Creation prints the complete high-entropy login key once. Distribute it out of
|
|
60
|
+
band through a secure channel; plaintext cannot be recovered from the database.
|
|
61
|
+
Login keys expire after one year. For a lost key, create a replacement and
|
|
62
|
+
revoke the old public key ID. Revocation also revokes the user's existing
|
|
63
|
+
sessions and OAuth families. Disable is terminal through the CLI; reactivation
|
|
64
|
+
requires a separately reviewed operator workflow.
|
|
65
|
+
|
|
66
|
+
The CLI rejects any flag a command does not take, for example
|
|
67
|
+
`auth key create --role admin`. It also rejects any unknown role name.
|
|
68
|
+
`auth user grant` changes only an existing user and never creates one. Both
|
|
69
|
+
`--role` and `--revoke-role` can be repeated.
|
|
70
|
+
|
|
71
|
+
## Roles and privileged operations
|
|
72
|
+
|
|
73
|
+
Scopes and roles do different jobs:
|
|
74
|
+
|
|
75
|
+
- **Scopes delegate.** A token's scopes are what the user let a client do on
|
|
76
|
+
their behalf. A user may consent to any supported scope, and issuing a token
|
|
77
|
+
never depends on roles.
|
|
78
|
+
- **Roles authorize.** A user has a set of roles. With no roles, the user is a
|
|
79
|
+
member:
|
|
80
|
+
|
|
81
|
+
| Role | Grants |
|
|
82
|
+
| ------------------ | --------------------------------------------------------------- |
|
|
83
|
+
| `admin` | `agents:admin`, `packages:approve` and `services:manage` |
|
|
84
|
+
| `package-approver` | `packages:approve` |
|
|
85
|
+
| `service-manager` | `services:manage` |
|
|
86
|
+
| (none) | nothing privileged; every other scope works as the token allows |
|
|
87
|
+
|
|
88
|
+
Five operations are privileged:
|
|
89
|
+
|
|
90
|
+
- `make_owner`, which reassigns any agent's owner, including another
|
|
91
|
+
principal's private agent (see [Sharing agents](#sharing-agents) for what
|
|
92
|
+
moves with it);
|
|
93
|
+
- setting a BYO `workerImageRef`;
|
|
94
|
+
- approving coding agents' package allowlists or policy;
|
|
95
|
+
- approving a repository for an agent without checking GitHub access
|
|
96
|
+
(`adminOverride` on `link_repository`, `repositoryAdminOverride` on
|
|
97
|
+
`create_agent`/`update_agent`; see [Repository access](#repository-access));
|
|
98
|
+
- creating, updating or deleting coding-run service catalog entries
|
|
99
|
+
(`create_service`, `update_service`, `delete_service`; reading the catalog
|
|
100
|
+
is `agents:read`, see [coding-services.md](coding-services.md)).
|
|
101
|
+
|
|
102
|
+
Each needs **both** its scope on the token **and** a role that grants that
|
|
103
|
+
permission:
|
|
104
|
+
|
|
105
|
+
- `make_owner`, `workerImageRef`, and repository approval need `agents:admin`.
|
|
106
|
+
- Package approval needs `packages:approve`, or `agents:admin`.
|
|
107
|
+
- Service catalog changes need `services:manage`.
|
|
108
|
+
|
|
109
|
+
In practice:
|
|
110
|
+
|
|
111
|
+
- an `admin` can do all five;
|
|
112
|
+
- a `package-approver` can approve packages only;
|
|
113
|
+
- a `service-manager` can change the service catalog only;
|
|
114
|
+
- a member can do none of them.
|
|
115
|
+
|
|
116
|
+
Callers who fail the check get `403`:
|
|
117
|
+
|
|
118
|
+
- A caller whose token holds the scope but whose roles don't grant it gets a
|
|
119
|
+
"requires a role" error. Authorizing again for more scopes can't fix this;
|
|
120
|
+
an operator has to grant a role.
|
|
121
|
+
- A caller who has a role granting the permission but whose token lacks the
|
|
122
|
+
scope gets the usual `insufficient_scope` challenge, naming the scope to
|
|
123
|
+
request. For package approval, that is the alternative their role grants.
|
|
124
|
+
|
|
125
|
+
Roles are read from the database on every request and are never cached.
|
|
126
|
+
|
|
127
|
+
**Any role change signs the user out.** This applies to both `--role` and
|
|
128
|
+
`--revoke-role`. In the same transaction, `auth user grant` revokes all of the
|
|
129
|
+
user's:
|
|
130
|
+
|
|
131
|
+
- OAuth grant families and refresh tokens;
|
|
132
|
+
- browser sessions;
|
|
133
|
+
- unexchanged authorization codes.
|
|
134
|
+
|
|
135
|
+
Every client must sign in and authorize again. The user sees the scopes afresh
|
|
136
|
+
under their new roles. This has two consequences:
|
|
137
|
+
|
|
138
|
+
- A token minted under the old roles can never regain that reach if a role is
|
|
139
|
+
granted again later.
|
|
140
|
+
- A grant the user consented to while a privileged scope was inert (for
|
|
141
|
+
example, an MCP client that requested every scope) never silently gains
|
|
142
|
+
privileged reach through a promotion.
|
|
143
|
+
|
|
144
|
+
A change that leaves the roles as they were, such as revoking a role the user
|
|
145
|
+
doesn't hold, revokes nothing.
|
|
146
|
+
|
|
147
|
+
The other scopes are not privileged. Every agent tool also checks the
|
|
148
|
+
caller's access to that agent, its owner or an explicit grant (see
|
|
149
|
+
[Sharing agents](#sharing-agents)); a scope never reaches an agent the caller
|
|
150
|
+
has no access to.
|
|
151
|
+
|
|
152
|
+
**Upgrading:** every existing self-hosted user starts with no roles, including
|
|
153
|
+
the operator. After deploying, grant yourself the admin role, then reconnect
|
|
154
|
+
your MCP client:
|
|
155
|
+
|
|
156
|
+
```sh
|
|
157
|
+
node dist/cli.js auth user grant --subject YOUR_SUBJECT --role admin
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
In delegated mode, roles come from a signed access-token claim that you map
|
|
161
|
+
with `AUTH_ROLE_CLAIM` and `AUTH_ROLE_MAP`. See
|
|
162
|
+
[Bring your own identity provider](getting-started-identity-provider.md#wardby-roles).
|
|
163
|
+
If you leave them unset, nobody has a role and the privileged operations are
|
|
164
|
+
refused over HTTP.
|
|
165
|
+
|
|
166
|
+
The browser login uses a single-use, cookie-bound challenge. Consent displays
|
|
167
|
+
the client, the canonical resource, and exactly the scopes approval issues: the
|
|
168
|
+
supported part of the request, fixed when the authorization request is created. Session tokens are
|
|
169
|
+
server-side hashes, with a 12-hour idle and 30-day absolute lifetime; HTTPS uses
|
|
170
|
+
`__Host-` cookies with Secure, HttpOnly, Path=/, and SameSite=Lax. Loopback-only
|
|
171
|
+
HTTP development uses explicitly named development cookies without Secure.
|
|
172
|
+
Sign-in rotates the token and revokes the previous presented session.
|
|
173
|
+
|
|
174
|
+
Auth forms require JavaScript. A nonce-protected same-origin fetch submits the
|
|
175
|
+
form while retaining Origin under `Referrer-Policy: no-referrer`; native form
|
|
176
|
+
POSTs otherwise send `Origin: null`. CSP permits only that per-response nonce
|
|
177
|
+
and same-origin connections. Null-origin requests remain rejected. This
|
|
178
|
+
behavior is covered by real Chrome, rather than only a synthetic cookie jar.
|
|
179
|
+
See the [Fetch standard](https://fetch.spec.whatwg.org/#append-a-request-origin-header).
|
|
180
|
+
|
|
181
|
+
Clients must re-register as public clients (`token_endpoint_auth_method=none`).
|
|
182
|
+
Only S256 PKCE and exact registered redirect strings are accepted. Redirects
|
|
183
|
+
must be HTTPS or loopback HTTP, without credentials or fragments. Registration
|
|
184
|
+
allows 1-10 redirects and a bounded client name. `AUTH_MAX_CLIENTS` defaults to
|
|
185
|
+
1000; PostgreSQL serializes capacity checks and shares rate limits across
|
|
186
|
+
processes. There is no confidential-client support or client secret.
|
|
187
|
+
|
|
188
|
+
Interactions expire after 10 minutes, authorization codes after 60 seconds,
|
|
189
|
+
access tokens after 10 minutes, and refresh families after 30 days. Code
|
|
190
|
+
consumption is atomic. Each refresh rotates its opaque token; reuse revokes the
|
|
191
|
+
whole family, including descendants. Access verification checks family and user
|
|
192
|
+
state, so revocation also invalidates existing access tokens. `/revoke` takes
|
|
193
|
+
the refresh token and public client ID. Do not retry a refresh with the same
|
|
194
|
+
token after an uncertain response; start a fresh authorization flow.
|
|
195
|
+
|
|
196
|
+
The server cleans expired auth rows every 15 minutes. Consumed refresh grants
|
|
197
|
+
remain until family expiry for reuse detection. Monitor cleanup failures.
|
|
198
|
+
Changing the credential hash key invalidates existing login keys, sessions,
|
|
199
|
+
codes, challenges, and refresh tokens. Rotate signing and credential keys
|
|
200
|
+
together during a planned global credential reset; provision new login keys
|
|
201
|
+
and reauthorize clients. Do not rotate `SECRET_APP_KEY` without a separate
|
|
202
|
+
encrypted-secret migration, or existing application secrets become unreadable.
|
|
203
|
+
|
|
204
|
+
## Sharing agents
|
|
205
|
+
|
|
206
|
+
An agent is private to its owner unless the owner shares it. A grant is
|
|
207
|
+
`(agent, grantee, level)`; the grantee is one principal or **everyone**:
|
|
208
|
+
|
|
209
|
+
| Level | Lets the grantee |
|
|
210
|
+
| --------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
211
|
+
| `read` | see the agent's config (never secret values, never other owners' tool code), list its sub-agents |
|
|
212
|
+
| `execute` | also trigger runs, and see the runs they triggered |
|
|
213
|
+
| `write` | also change its name, prompt, model, budget amount, max turns, effort, memory on/off and schedule; attach tools they can use and detach tools; create webhooks |
|
|
214
|
+
| owner | everything, plus the owner-only operations below |
|
|
215
|
+
|
|
216
|
+
Owners manage grants with `grant_access`, `revoke_access` and `list_access`.
|
|
217
|
+
Granting again replaces the level. A revoke takes effect on the next check
|
|
218
|
+
(trigger, delegation, webhook fire); runs already in flight keep going.
|
|
219
|
+
|
|
220
|
+
**Owner-only, whatever the grants:** `delete_agent`, managing grants, the
|
|
221
|
+
agent's secret, datastore and repository bindings, the whole coding profile
|
|
222
|
+
(task, base ref, protected paths, task-override opt-in, image, packages,
|
|
223
|
+
toolchain, limits), changing its kind, its budget group, attaching or
|
|
224
|
+
detaching sub-agents, reading or writing its memory and datastore contents,
|
|
225
|
+
and granting capabilities to a tool attachment.
|
|
226
|
+
|
|
227
|
+
**What execute hands over.** An execute-grantee runs your agent with your
|
|
228
|
+
tools, secrets, datastores and repository, and every run shares the agent's
|
|
229
|
+
memory. A non-owner supplies no instructions of their own: a native agent
|
|
230
|
+
takes no task text from `trigger_agent` or from another owner's sub-agent
|
|
231
|
+
delegation, and a coding agent takes a `task` or `baseRef` from a non-owner
|
|
232
|
+
only if `codingProfile.allowWebhookTaskOverride` is on (the same opt-in
|
|
233
|
+
webhooks use); otherwise the run uses your `defaultTask`. Webhooks still
|
|
234
|
+
accept task text for native agents (the webhook creator needs `write` to make
|
|
235
|
+
one). Runs are visible to their triggerer and to you, never to other
|
|
236
|
+
grantees.
|
|
237
|
+
|
|
238
|
+
**What write hands over.** A write-grantee can rewrite what the agent's runs
|
|
239
|
+
do, but not add to what they can reach. The prompt can make a run use
|
|
240
|
+
everything the agent already reaches: the tool capabilities you granted, its
|
|
241
|
+
memory, and its linked repositories. On a coding agent the prompt is part of
|
|
242
|
+
every coding task, so write directs work done with your GitHub access; grant
|
|
243
|
+
write on such an agent only to someone you'd let push. What stays yours: the
|
|
244
|
+
four capability fields on a tool attachment (`allowedSecrets`,
|
|
245
|
+
`allowedDatastorePrefixes`, `allowedHosts`, `allowedSharedDatastorePrefixes`),
|
|
246
|
+
so a tool a write-grantee attaches runs with none until you re-run
|
|
247
|
+
`attach_tool` with them (you then vouch for code only its owner can read);
|
|
248
|
+
the coding profile, kind and budget group; and sub-agents, since an edge
|
|
249
|
+
would hand every run's data to another agent.
|
|
250
|
+
|
|
251
|
+
**Everyone** can be granted at most `execute`: everyone-write would let
|
|
252
|
+
anyone rewrite an agent that holds your tools and secrets.
|
|
253
|
+
|
|
254
|
+
**Nothing crosses owners without a grant on that exact resource**, checked
|
|
255
|
+
when bindings are written and again at run time, so rows written before this
|
|
256
|
+
rule (or left behind by `make_owner`) stop working on their own:
|
|
257
|
+
|
|
258
|
+
- a secret or datastore binding resolves only while the resource's owner is
|
|
259
|
+
the agent's current owner; any other binding behaves like an unattached
|
|
260
|
+
name;
|
|
261
|
+
- a tool attachment's capabilities count only while the agent's current owner
|
|
262
|
+
granted them;
|
|
263
|
+
- a sub-agent runs only if it has the parent's owner, or the parent's owner
|
|
264
|
+
holds `execute` on it (`attach_subagent` is the parent's owner's, with
|
|
265
|
+
`execute` on the child). Across owners, the edge carries no memory access,
|
|
266
|
+
no `grantParentMemoryKeys`, no `continuePriorRun`, no task text for a
|
|
267
|
+
native child (it runs its own prompt), and no coding task unless the child
|
|
268
|
+
allows non-owner task text. The child's owner can always detach it;
|
|
269
|
+
- a webhook fires only while its creator owns the agent or holds `execute`.
|
|
270
|
+
|
|
271
|
+
**The stdio operator** (`wardby mcp` over stdio) is owner of every agent for
|
|
272
|
+
access checks and sees every agent, but does not bypass the binding rules:
|
|
273
|
+
it can't bind its own secret to someone else's agent, and it is not the owner
|
|
274
|
+
for coding-profile changes, coding task overrides or sub-agent edges on
|
|
275
|
+
someone else's agent (it can adopt the agent first). An HTTP user whose
|
|
276
|
+
subject equals `LOCAL_PRINCIPAL` is not the operator. **Admins** get no
|
|
277
|
+
implicit access to others' agents: they can `list_access` any agent (incident
|
|
278
|
+
response) and reassign one with `make_owner`, which removes bindings the new
|
|
279
|
+
owner doesn't own, suspends tool capabilities the new owner never granted,
|
|
280
|
+
cuts sub-agent edges the new owner can't delegate, and, between two owners,
|
|
281
|
+
resets every grant. Adopting an owner-less agent keeps its grants but lowers
|
|
282
|
+
an everyone grant to `read` unless `keepEveryoneExecute` is set.
|
|
283
|
+
`make_owner` no longer releases an agent to owner-less.
|
|
284
|
+
|
|
285
|
+
### Upgrading: owner-less agents
|
|
286
|
+
|
|
287
|
+
Before this release, an agent with no owner was public: anyone could read
|
|
288
|
+
**and change** it. The migration keeps such agents runnable by giving each an
|
|
289
|
+
everyone-`execute` grant; nobody but the stdio operator can edit them until
|
|
290
|
+
they get an owner. Until then they run without owned secrets or datastores and
|
|
291
|
+
without their tools' capabilities. Operator runbook:
|
|
292
|
+
|
|
293
|
+
1. On the old version, with the new binary:
|
|
294
|
+
`wardby grants migration-report > before.txt`. It is read-only and lists
|
|
295
|
+
owner-less agents (and, per possible adopter, which secrets, datastores
|
|
296
|
+
and tool capabilities adopting would bring back), the bindings and
|
|
297
|
+
capabilities that will stop working, owner-less-tool capabilities that
|
|
298
|
+
stay in force on owned agents (review them if the agent ever changed owner
|
|
299
|
+
with `make_owner`), sub-agent edges that will be refused, webhooks that
|
|
300
|
+
won't fire, and the behaviour changes.
|
|
301
|
+
2. `prisma migrate deploy` (`npm run prisma:migrate`), then roll out. The
|
|
302
|
+
migration needs PostgreSQL 13 or later.
|
|
303
|
+
3. Immediately: `wardby grants adopt-public --owner <subject> --dry-run`,
|
|
304
|
+
then the same without `--dry-run`. Every owner-less agent gets that owner;
|
|
305
|
+
its everyone grant is lowered to `read`, because the agent is about to
|
|
306
|
+
regain the owner's secrets and capabilities (pass
|
|
307
|
+
`--keep-everyone-execute` to keep it runnable by everyone, knowingly;
|
|
308
|
+
webhooks others created stop firing otherwise). The owner's own bindings
|
|
309
|
+
come back to life, the others are removed and printed; attachments of the
|
|
310
|
+
owner's own tools regain their capabilities; every other attached tool is
|
|
311
|
+
printed for review, with the `attach_tool` call that re-grants any
|
|
312
|
+
capabilities it had. The subject must already exist.
|
|
313
|
+
4. `wardby grants prune-bindings` removes, and prints, every remaining
|
|
314
|
+
binding of one owner's secret or datastore to another owner's agent
|
|
315
|
+
(already inert).
|
|
316
|
+
5. `wardby grants migration-report > after.txt`.
|
|
317
|
+
|
|
318
|
+
`wardby agent create` now owns the agent by `--owner <subject>` or
|
|
319
|
+
`LOCAL_PRINCIPAL` (`--public` adds everyone-`execute`), `wardby tool attach`
|
|
320
|
+
grants capabilities in the agent owner's name, and `wardby import --public`
|
|
321
|
+
owns the imported agents by `--owner` or the importing operator, shared with
|
|
322
|
+
everyone at `execute`.
|
|
323
|
+
|
|
324
|
+
## Repository access
|
|
325
|
+
|
|
326
|
+
The GitHub App is installed on repositories by their owners, not per wardby
|
|
327
|
+
user, so "the App can reach it" is not permission to use it. An agent may use
|
|
328
|
+
a repository — a `link_repository` link, or a coding agent's
|
|
329
|
+
`codingProfile.repository` — only when:
|
|
330
|
+
|
|
331
|
+
- its **current owner's** GitHub account, linked with `link_host_account`, has
|
|
332
|
+
enough permission on it: write for coding repositories and write links
|
|
333
|
+
(they push, comment, and publish checks), read for read links; or
|
|
334
|
+
- a wardby `admin` approved that exact repository explicitly; the approval is
|
|
335
|
+
recorded (who, when); or
|
|
336
|
+
- the authorization predates enforcement (`grandfathered`, stamped by the
|
|
337
|
+
migration).
|
|
338
|
+
|
|
339
|
+
Trust assumptions:
|
|
340
|
+
|
|
341
|
+
- **The owner's access decides, not the trigger's.** Whoever triggers a run (a
|
|
342
|
+
schedule, a webhook, a host event, the owner, an `execute`-grantee) only
|
|
343
|
+
supplies task text, and a non-owner supplies coding task text only when the
|
|
344
|
+
owner enabled `allowWebhookTaskOverride`; the owner controls the tools,
|
|
345
|
+
secrets and repository. A repository is a binding: only the owner (or an
|
|
346
|
+
admin's explicit approval) sets it, never a `write`-grantee.
|
|
347
|
+
- **Owner-less agents never hold a repository**, even admin-approved: there is
|
|
348
|
+
no owner whose GitHub access can be checked. Assign an owner first.
|
|
349
|
+
- **Checked where it's used.** Every coding run (before its workspace is
|
|
350
|
+
prepared, and again right before it pushes), every `repo_*` call, and every
|
|
351
|
+
host-event dispatch re-checks a `host_permission` authorization against the
|
|
352
|
+
current owner, with a 5-minute cache. A GitHub error refuses the use; for a
|
|
353
|
+
run already under way, a transient error (5xx, timeout, rate limit) is
|
|
354
|
+
retried once, then refused with its own category
|
|
355
|
+
(`repo_access_unavailable`). Set-time checks never retry.
|
|
356
|
+
- **Approvals stay with the owner they were granted under.** Admin and
|
|
357
|
+
grandfathered authorizations are not re-checked while the agent keeps its
|
|
358
|
+
owner; revoke them by unlinking or changing the repository. `make_owner` to
|
|
359
|
+
a different owner converts them into checks of the next owner's own GitHub
|
|
360
|
+
access; only an owner-less agent's first owner keeps them.
|
|
361
|
+
- **Admins approve explicitly, on any owned agent.** `adminOverride` works on
|
|
362
|
+
agents the admin doesn't own (the admin role can already reassign any
|
|
363
|
+
agent); it is always recorded with the approver. On another's agent an
|
|
364
|
+
admin can change only the repository.
|
|
365
|
+
- **Public repositories:** GitHub reports read access for everyone, so any
|
|
366
|
+
linked principal may create a `read` link to a public repository the App is
|
|
367
|
+
installed on. Only public data is exposed that way.
|
|
368
|
+
- **Identity is proven, not claimed.** Linking uses the App's OAuth web flow
|
|
369
|
+
with single-use state, S256 PKCE, and a one-time confirmation code that
|
|
370
|
+
only the initiating principal can submit, so a victim clicking an attacker's
|
|
371
|
+
link can't bind their GitHub account to the attacker. wardby keeps only the
|
|
372
|
+
GitHub numeric user id and login; the user token is revoked immediately.
|
|
373
|
+
Permission checks use the App's installation token. One GitHub account links
|
|
374
|
+
to one principal.
|
|
375
|
+
- **Mentions need push access.** An `@<app-slug>` mention runs an agent only
|
|
376
|
+
when its author has write access to the repository, checked live by user id.
|
|
377
|
+
- **Checks are bound to the dispatched PR.** A review publishes a check only on
|
|
378
|
+
the pull request its run was dispatched for; fork PRs are never dispatched.
|
|
379
|
+
|
|
380
|
+
Before upgrading, find owner-less agents that have a repository link or a
|
|
381
|
+
coding profile (`list_agents`, `list_repositories`) and give each an owner
|
|
382
|
+
with `make_owner` (or `wardby grants adopt-public`), or they stop running. See
|
|
383
|
+
[code-review-agents.md](code-review-agents.md#who-may-give-an-agent-a-repository)
|
|
384
|
+
for the App settings (callback URL, client ID and secret).
|
|
385
|
+
|
|
386
|
+
## Migration and rollback
|
|
387
|
+
|
|
388
|
+
Migration `20260906040000_secure_self_hosted_oauth` deliberately drops the two
|
|
389
|
+
legacy OAuth tables and recreates incompatible secure state. All legacy
|
|
390
|
+
clients, codes, access tokens, refresh tokens, and login credentials must be
|
|
391
|
+
reissued. The verifier also rejects the legacy token shape, even with retained
|
|
392
|
+
signing material. No application principal or owned business data is deleted.
|
|
393
|
+
|
|
394
|
+
Stop issuance, take a backup of unrelated data, and use `prisma migrate deploy`
|
|
395
|
+
from a trusted migration job. Never use `db push` or edit an applied migration.
|
|
396
|
+
The schema adds users, hashed login keys, sessions, form challenges,
|
|
397
|
+
authorization interactions/codes, serialized refresh families/grants, and
|
|
398
|
+
shared throttling. Foreign keys and indexes are declared in both SQL and Prisma.
|
|
399
|
+
|
|
400
|
+
Restoring a full pre-fix backup would resurrect usable compromised credentials.
|
|
401
|
+
The secure recovery alternative restores unrelated tables only, with issuance
|
|
402
|
+
stopped. For a schema rollback, recreate empty legacy OAuth placeholders
|
|
403
|
+
before reapplying the security migration; never restore their contents. The
|
|
404
|
+
rehearsal script exercises migration, unrelated-data restoration, and
|
|
405
|
+
re-migration against synthetic data in an isolated container:
|
|
406
|
+
|
|
407
|
+
```sh
|
|
408
|
+
node scripts/security-migration-rehearsal.mjs
|
|
409
|
+
```
|
|
410
|
+
|
|
411
|
+
That script deliberately targets only container `wardby-security-20260906`,
|
|
412
|
+
database `wardby_security`, and freshly generated temporary schemas. Its
|
|
413
|
+
temporary schemas are removed after verification. It is not a production
|
|
414
|
+
backup or rollback command. Have a second reviewer inspect production backup,
|
|
415
|
+
recovery, auth identity derivation, CSRF, transactions, and SSRF before rollout.
|
|
416
|
+
|
|
417
|
+
## Durable executor
|
|
418
|
+
|
|
419
|
+
`EXECUTOR=dbos` tables live outside Prisma's migration chain: DBOS creates and
|
|
420
|
+
migrates its own `dbos` schema (`DBOS_SCHEMA`) at `launch()`. On a fresh
|
|
421
|
+
schema that starts with `CREATE SCHEMA IF NOT EXISTS`, which Postgres checks
|
|
422
|
+
against `CREATE` on the _database_ even when the schema already exists, so a
|
|
423
|
+
server role limited to data access cannot launch DBOS on its own. Either give
|
|
424
|
+
the server's role that privilege, or migrate the schema out of band as a
|
|
425
|
+
privileged role with `npm run dbos:migrate` (`dbos schema "$DATABASE_URL"`)
|
|
426
|
+
and grant the server's role `USAGE` on the schema plus `SELECT`, `INSERT`,
|
|
427
|
+
`UPDATE` and `DELETE` on its tables, with default privileges for tables a
|
|
428
|
+
later SDK version adds. Once the schema is current, `launch()` changes
|
|
429
|
+
nothing. The GKE module does the latter (`deploy/gke/database-grants.sql`).
|
|
430
|
+
|
|
431
|
+
`DBOS_EXECUTOR_ID` must be unique per running _process_ — two processes
|
|
432
|
+
sharing an id each believe they own the other's in-flight runs and re-drive
|
|
433
|
+
them at launch. Left unset, each process generates a random one, which is the
|
|
434
|
+
safe default: a process that replaces a dead one does not need its id,
|
|
435
|
+
because the reconciler adopts any live workflow whose run's heartbeat has gone
|
|
436
|
+
stale, whichever executor id owns it. Set a fixed id only for a single,
|
|
437
|
+
long-lived process that should re-drive its own runs at launch rather than
|
|
438
|
+
after the heartbeat timeout.
|
|
439
|
+
|
|
440
|
+
**New data at rest.** Durable execution checkpoints every step's result into
|
|
441
|
+
`dbos.operation_outputs`: each turn's assistant text, every tool call's
|
|
442
|
+
arguments, every tool result in full, and the `load` step's pinned agent
|
|
443
|
+
fields plus each attached tool's source and `paramsZod`. That is the same
|
|
444
|
+
class of data as the `Run`/`Tool` tables — prompt content, tool output, and
|
|
445
|
+
anything a tool returned from a secret-bearing call — and it accumulates for
|
|
446
|
+
the life of every workflow record, with no retention limit of its own. Give
|
|
447
|
+
the `dbos` schema the same encryption-at-rest, access control, backup, and
|
|
448
|
+
backup-retention handling as the application schema, and prune completed
|
|
449
|
+
workflows periodically: `DBOS.deleteWorkflow(workflowID, deleteChildren?)`
|
|
450
|
+
removes one workflow and its step records (irreversible), or delete rows
|
|
451
|
+
directly from the schema's tables for a bulk retention job. Prune only
|
|
452
|
+
workflows in a finished status — deleting a PENDING record makes its run
|
|
453
|
+
unrecoverable, and the reconciler will reap it as `lost`.
|
|
454
|
+
|
|
455
|
+
**Version pinning across upgrades.** DBOS gates both recovery and dequeue on
|
|
456
|
+
`application_version`, which it computes from the registered code unless
|
|
457
|
+
`DBOS__APPVERSION` is set. After an SDK bump or any change to the workflow
|
|
458
|
+
wrapper, workflows recorded under the old version are never dequeued by the
|
|
459
|
+
new process: pin `DBOS__APPVERSION` per deploy, or drain in-flight runs before
|
|
460
|
+
upgrading. The executor no longer loops on those runs — `recover()` reports
|
|
461
|
+
the run `lost` as soon as it sees a version mismatch, and otherwise gives up
|
|
462
|
+
after three adoption attempts. That attempt counter is in memory and per
|
|
463
|
+
process, so it resets on restart.
|
|
464
|
+
|
|
465
|
+
**Rolling back, and rolling forward again.** Rolling back to
|
|
466
|
+
`EXECUTOR=in-process` is safe at any time: any DBOS run still in flight is
|
|
467
|
+
reconciled to `lost` once its heartbeat times out, because no executor is left
|
|
468
|
+
to recover it, and nothing else in the deployment depends on the `dbos`
|
|
469
|
+
schema. The return trip is the part to know about: those workflows are still
|
|
470
|
+
PENDING in the `dbos` schema, and re-enabling `EXECUTOR=dbos` with the same
|
|
471
|
+
executor id re-drives them at launch. (With a random per-process id, nothing
|
|
472
|
+
re-drives them: they stay PENDING until pruned.) They no longer re-spend — `executeRun`
|
|
473
|
+
short-circuits on any run whose row is already terminal, so a re-driven
|
|
474
|
+
workflow whose run was reaped as `lost` does no work and leaves the row
|
|
475
|
+
`lost` — but the workflows do wake up and run to completion in DBOS's own
|
|
476
|
+
records. Delete them (see pruning above) if you want the rollback to be final.
|
|
477
|
+
|
|
478
|
+
## Resource and networking limits
|
|
479
|
+
|
|
480
|
+
| Surface | Limit |
|
|
481
|
+
| ------------------------------ | ------------------------------------------------- |
|
|
482
|
+
| MCP/webhook bodies | 1 MiB raw bytes |
|
|
483
|
+
| Auth bodies | 64 KiB raw bytes |
|
|
484
|
+
| Request completion / headers | 15 seconds / 10 seconds |
|
|
485
|
+
| Sandbox fetch | 8 MiB encoded and decoded, 5 redirects, 8 seconds |
|
|
486
|
+
| Bridge input / output | 1 MiB / 12 MiB |
|
|
487
|
+
| Parser input / console payload | 256 KiB / 16 KiB |
|
|
488
|
+
| HTML links extracted | 1000, with incremental result-byte accounting |
|
|
489
|
+
| Random bytes | 65,536 bytes |
|
|
490
|
+
| Host calls | 256 per invocation, at most 8 pending |
|
|
491
|
+
| Datastore value / keys listed | 1 MiB / 1000 |
|
|
492
|
+
| Datastore key / secret name | 1024 bytes |
|
|
493
|
+
| New secret plaintext | 64 KiB |
|
|
494
|
+
|
|
495
|
+
Oversized or malformed input fails explicitly. Existing oversized datastore
|
|
496
|
+
values/keys and secret ciphertext fail instead of entering Node allocations.
|
|
497
|
+
Response limits also apply after decompression. Fetch validates every redirect
|
|
498
|
+
and pins the validated DNS result to the connection while retaining Host and
|
|
499
|
+
TLS verification. Cross-origin redirects remove credentials, including custom
|
|
500
|
+
API-key headers; forwarding a request body across origins is rejected.
|
|
501
|
+
|
|
502
|
+
Every resolved address must be global. A tool's own `allowedHosts` only
|
|
503
|
+
narrows egress; it never opens a private, loopback or link-local destination.
|
|
504
|
+
Only the operator's `WARDBY_FETCH_ALLOWED_HOSTS` (exact normalized hosts) can,
|
|
505
|
+
and a non-wildcard tool must list the host too. Cloud metadata endpoints
|
|
506
|
+
(`169.254.169.254`, `169.254.169.252`, `169.254.170.2`, `169.254.170.23`,
|
|
507
|
+
`100.100.100.200`, `fd00:ec2::254`, `fd00:ec2::23`, `fd20:ce::254`, their
|
|
508
|
+
mapped/NAT64/6to4 IPv6 forms, `metadata.google.internal`, `metadata`) are
|
|
509
|
+
always blocked, even if listed.
|
|
510
|
+
The network layer cannot back this up for the control plane on GKE, because
|
|
511
|
+
Workload Identity needs the metadata server.
|
|
512
|
+
Sandbox code can explicitly log secrets it has been given; log size limits do
|
|
513
|
+
not provide automatic redaction. Keep tool-authoring privileges restricted.
|
|
514
|
+
Public/non-owner tool listings expose only id/name/description; owners retain
|
|
515
|
+
their source and schema. Private agents are indistinguishable from missing IDs.
|
|
516
|
+
|
|
517
|
+
Cancellation stops bridge delivery, sleep timers, and in-flight network work.
|
|
518
|
+
Prisma's query interface does not accept an AbortSignal (still true on Prisma
|
|
519
|
+
7's `pg` driver adapter): an already-dispatched database operation may finish
|
|
520
|
+
after sandbox cancellation. Its returned data is
|
|
521
|
+
bounded before entering Node, but cancellation is not a transaction rollback.
|
|
522
|
+
Set a short database statement/lock timeout on the dedicated application role,
|
|
523
|
+
bound connection pools and concurrent invocations, and review a cancellable
|
|
524
|
+
database adapter or isolated worker if strict immediate termination is required.
|
|
525
|
+
Do not claim these per-invocation caps establish a whole-process memory ceiling.
|
|
526
|
+
|
|
527
|
+
## Coding runs on Kubernetes
|
|
528
|
+
|
|
529
|
+
`JOB_LAUNCHER=kubernetes` runs each coding agent in its own pod instead of a
|
|
530
|
+
container. The trust model is unchanged — the worker is untrusted, holds no
|
|
531
|
+
provider credential, and may reach only the coding proxy — but a cluster
|
|
532
|
+
enforces that differently from a Docker host, and two controls exist because a
|
|
533
|
+
cluster's configuration cannot be taken on trust.
|
|
534
|
+
|
|
535
|
+
**The pod is attested before the worker runs.** The pod and NetworkPolicy read
|
|
536
|
+
back from the API server are compared field by field against what wardby built.
|
|
537
|
+
Any difference fails the launch closed. This is what catches a mutating
|
|
538
|
+
admission controller, or anyone with cluster access, weakening a pod's isolation
|
|
539
|
+
between creation and start. Only an explicit list of known server defaults is
|
|
540
|
+
normalized away; nothing else is tolerated.
|
|
541
|
+
|
|
542
|
+
**Enforcement is proven, not assumed.** A cluster accepts a NetworkPolicy
|
|
543
|
+
whether or not its CNI enforces one, and even where enforcement works it is
|
|
544
|
+
programmed seconds after a pod starts. Before releasing the worker, the launcher
|
|
545
|
+
connects from inside the pod to a destination that must be blocked and requires
|
|
546
|
+
three consecutive refusals. A cluster that does not enforce policy, or has not
|
|
547
|
+
yet programmed this pod's rules, never reaches the point of running agent code.
|
|
548
|
+
The same check runs at startup against a throwaway canary pod, which also
|
|
549
|
+
verifies the pod cannot reach DNS, the internet or the cloud metadata endpoint.
|
|
550
|
+
|
|
551
|
+
**RBAC.** The control plane needs a namespace Role (`pods`, `pods/exec`,
|
|
552
|
+
`pods/log`, `secrets`, `configmaps`, `networkpolicies`, `services`) and a
|
|
553
|
+
ClusterRole granting `get` on the single namespace it runs in, because the
|
|
554
|
+
preflight's namespace read is cluster-scoped. Workers get a dedicated
|
|
555
|
+
ServiceAccount with no permissions and no mounted token. Reference manifests are
|
|
556
|
+
in `deploy/kind-coding/manifests/`.
|
|
557
|
+
|
|
558
|
+
**Sandboxing.** The spec requires gVisor on GKE; the reference `kind` harness
|
|
559
|
+
has none, so that configuration is development-only and says so. Set
|
|
560
|
+
`KUBERNETES_RUNTIME_CLASS` to the cluster's sandboxed runtime class in
|
|
561
|
+
production; the launcher warns loudly when it is unset.
|
|
562
|
+
|
|
563
|
+
The `gke-autopilot` platform profile contains narrow admission allowances
|
|
564
|
+
captured from a real cluster and requires the `gvisor` runtime class. A dry-run
|
|
565
|
+
capture cannot observe labels added after pod scheduling, so live-run behavior
|
|
566
|
+
is pinned separately by tests. Run `wardby coding preflight` against every
|
|
567
|
+
target cluster before accepting work; it fails closed when admission or network
|
|
568
|
+
behavior differs from the reviewed profile.
|
|
569
|
+
|
|
570
|
+
Before production deployment, review the
|
|
571
|
+
[current Kubernetes limitations](coding-worker-isolation.md#known-limitations).
|
|
572
|
+
Per-run record ConfigMaps currently require operator-managed garbage collection,
|
|
573
|
+
and the real-cluster suite does not yet cover every Docker containment
|
|
574
|
+
scenario.
|
|
575
|
+
|
|
576
|
+
## Coding package registry
|
|
577
|
+
|
|
578
|
+
Enabling a coding agent's package allowlist widens the trusted proxy's own
|
|
579
|
+
egress, not the worker's: the proxy (the only host any worker can reach) is
|
|
580
|
+
additionally permitted to reach `registry.npmjs.org`, `pypi.org`,
|
|
581
|
+
`files.pythonhosted.org`, and `api.osv.dev`, through the same pinned HTTPS
|
|
582
|
+
fetch used for everything else it calls out to (no redirects, no private or
|
|
583
|
+
IP-literal addresses). The worker's own network reachability is unchanged: it
|
|
584
|
+
still reaches only `wardby-proxy:8787`. See
|
|
585
|
+
[Installing packages in coding runs](coding-packages.md) for the full model,
|
|
586
|
+
including its per-run download limits.
|
|
587
|
+
|
|
588
|
+
Two of that page's safeguards are worth calling out for an operator: npm
|
|
589
|
+
install scripts are disabled only by a configuration default
|
|
590
|
+
(`npm_config_ignore_scripts=true`) that the proxy cannot enforce against a
|
|
591
|
+
downloaded tarball, so treat it as a documented limit rather than a
|
|
592
|
+
guarantee; and the OSV vulnerability audit fails **closed**
|
|
593
|
+
(`503 wardby_audit_unavailable`) when OSV can't be reached, unless the
|
|
594
|
+
operator explicitly sets `REGISTRY_AUDIT_FAIL_OPEN=true` to allow installs
|
|
595
|
+
through unaudited during an OSV outage.
|
|
596
|
+
|
|
597
|
+
## Images and dependencies
|
|
598
|
+
|
|
599
|
+
```sh
|
|
600
|
+
docker build -f deploy/Dockerfile --target runtime -t wardby-runtime .
|
|
601
|
+
docker build -f deploy/Dockerfile --target migration -t wardby-migration .
|
|
602
|
+
```
|
|
603
|
+
|
|
604
|
+
The runtime runs as uid 1000 and contains the generated Prisma client (compiled
|
|
605
|
+
into `dist/generated/prisma`) with `@prisma/client`'s runtime and the `pg`
|
|
606
|
+
driver adapter, but no Prisma CLI, `@prisma/config`, `deepmerge-ts` or `mysql2`
|
|
607
|
+
(the image build asserts this). Its manifest drops development dependencies and
|
|
608
|
+
omits optional peers before installation: `@prisma/client` declares the CLI as
|
|
609
|
+
an optional peer. The migration image keeps the full build tree -- the Prisma
|
|
610
|
+
CLI and `prisma.config.ts`.
|
|
611
|
+
|
|
612
|
+
**Development tree (repository, CI, build and migration images).** The Prisma
|
|
613
|
+
CLI (a devDependency since the Prisma 7 upgrade) still carries two flagged
|
|
614
|
+
packages, both forced to patched versions by `overrides` in `package.json`:
|
|
615
|
+
|
|
616
|
+
```json
|
|
617
|
+
"overrides": { "deepmerge-ts": "8.0.2", "mysql2": "3.24.4" }
|
|
618
|
+
```
|
|
619
|
+
|
|
620
|
+
- `deepmerge-ts`: `@prisma/config` 7.10.0 still pins 7.1.5 exactly. The 8.x
|
|
621
|
+
override is safe because `@prisma/config` calls only `deepmerge()`; the 8.0
|
|
622
|
+
breaking changes are two type renames and `deepmergeInto`, which Prisma does
|
|
623
|
+
not call.
|
|
624
|
+
- `mysql2` (GHSA-3f6p-5ww8-9rcr, GHSA-rgwj-5xj2-c3m3, high): `prisma` 7.10.0
|
|
625
|
+
pins 3.15.3 exactly; it is used only by Prisma Studio's MySQL executor
|
|
626
|
+
(`createPool` from `mysql2/promise`), never by wardby, which is
|
|
627
|
+
PostgreSQL-only. 3.24.4 is the same major. After changing either override,
|
|
628
|
+
re-run `prisma generate`, `prisma validate`, the `migrate diff` drift check,
|
|
629
|
+
and the migration image's `migrate deploy`.
|
|
630
|
+
|
|
631
|
+
`scripts/security-audit.mjs` carries no exceptions: with the overrides the full
|
|
632
|
+
and production audits are clean, and if a lockfile change ever dropped one, the
|
|
633
|
+
advisory would reappear and the security job would fail.
|
|
634
|
+
|
|
635
|
+
Prisma's npm `latest` dist-tag currently points at an 8.0 release candidate, so
|
|
636
|
+
pin exact versions (the build hides the CLI's update banner, which recommends
|
|
637
|
+
it).
|
|
638
|
+
|
|
639
|
+
Vitest and esbuild's additional development advisories were remediated by
|
|
640
|
+
supported tooling updates.
|
|
641
|
+
|
|
642
|
+
## Verification and rollout
|
|
643
|
+
|
|
644
|
+
Install the test browser with `npx playwright install chromium`; on this
|
|
645
|
+
machine `SECURITY_BROWSER_CHANNEL=chrome` uses installed Chrome in an isolated
|
|
646
|
+
headless profile. Database-backed and browser tests are not release evidence
|
|
647
|
+
when skipped. Use a disposable `DATABASE_URL`, then run typecheck, full tests,
|
|
648
|
+
contract tests, build, Prisma validation, migration replay/drift/recovery, and
|
|
649
|
+
`node scripts/security-audit.mjs`. The CI job runs database tests and archives a dependency tree.
|
|
650
|
+
After building, `node --expose-gc scripts/security-allocation-check.mjs` checks
|
|
651
|
+
stream abortion, read-ahead, and process-memory growth for an endless response.
|
|
652
|
+
|
|
653
|
+
Before external rollout, verify proxy TLS/Host normalization, blocked direct
|
|
654
|
+
ingress, egress policy, least-privilege encrypted database access, secret-manager
|
|
655
|
+
injection, credential-free access logs, and alerts for 401/403/413 responses,
|
|
656
|
+
blocked fetches, cleanup failures, and memory pressure. These deployment gates
|
|
657
|
+
cannot be established by local source tests alone.
|