Repository navigation
Expand file tree
/
Copy pathwrangler.jsonc
More file actions
263 lines (245 loc) · 12.2 KB
/
Copy pathwrangler.jsonc
File metadata and controls
263 lines (245 loc) · 12.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
{
// FlareDispatch Dispatcher Worker (new-repo deploy).
// Non-destructive: distinct name + fresh D1/R2/KV so it never collides with
// any prior deployment in the same account. Dashboard SPA assets are omitted
// (the SSR fallback in dashboard.ts serves the routes).
//
// Account-specific values below are placeholders marked `operator overlay`.
// A deploying org replaces this whole file with its own copy carrying the
// real ones (actions/deploy-dispatcher-action). Locally, keep that copy as
// the gitignored wrangler.overlay.local.jsonc and pass it with
// `pnpm exec wrangler deploy -c wrangler.overlay.local.jsonc`.
"$schema": "node_modules/wrangler/config-schema.json",
"name": "flare-dispatch",
"main": "apps/dispatcher/src/index.ts",
"compatibility_date": "2026-05-01",
"compatibility_flags": ["nodejs_compat"],
"observability": { "enabled": true },
"keep_vars": true,
// A Workflow instance's subrequests accumulate across its steps. A release
// cell polls a multi-hour cross-build across dozens of steps, each spending
// sandbox, R2 and D1 calls, so the Workers Paid default of 10,000 per
// instance ends a long build at its publish steps. 10,000,000 is the Workers
// Paid ceiling. The deploy merges this block into the operator overlay
// (infra/overlay-limits.mjs), so an overlay need not restate it.
"limits": { "subrequests": 10000000 },
// Wall-clock dispatch. Each expression must match a registered run's
// `schedules[].cron` EXACTLY — `schedulesByCron` (apps/dispatcher/src/registry.ts)
// matches on the literal string, so a cosmetic difference silently fires
// nothing. `handleScheduled` logs a warning when a cron matches no run,
// which is the only symptom drift produces.
//
// Nothing here fired before this block existed: the runs declared schedules,
// the `scheduled()` handler was wired and unit-tested, and the Worker was
// simply never asked to wake up. Three built runs had been producing no
// signal since they landed.
"triggers": {
"crons": [
"0 5 * * *", // spec-drift-pr — daily
"45 5 * * *", // org-spec-audit — daily, after spec-drift-pr
"0 6 * * *", // ci-triage-pr — daily
"0 7 * * 1", // finops-audit — Mondays
"17 2 * * *", // contextful-measures — daily
"17 3 * * SUN", // contextful-protocol — Sunday at 03:17 UTC
],
},
// Two further runs declare schedules and are deliberately NOT armed here,
// because arming them is a product decision rather than a config omission:
//
// "0 9 * * 1" release-notes — opens a NON-draft PR and holds a 72h human
// approval gate. First firing puts a release PR in front of
// people who have not agreed to review one weekly.
// "0 14 * * *" product-demo — daily browser-rendering run; its confirmed
// failures escalate into self-heal-pr. Recurring spend plus an
// auto-PR path, both worth switching on deliberately.
//
// Add the expression here when that decision is made; the runs need no change.
// Custom domain (requires its zone in the deploying account). The GitHub App
// install flow substitutes the request origin into the manifest, so hitting
// the install page on this host wires the App's webhook/callback URLs to it.
// Hostname: operator overlay.
"routes": [{ "pattern": "dispatcher.example.com", "custom_domain": true }],
// Workers AI — backs the modelGateway capability the pr-review engine calls.
// The binding IS the auth (account-billed), so no model API key is needed.
"ai": { "binding": "AI" },
// Browser Rendering — backs GET /v1/browser/cdp (unused by pr-review; kept
// for parity with the run set).
"browser": { "binding": "BROWSER" },
// The deployed version's id — logged by a run at each spawn beside its
// execution's creation time, so an instance created before a deploy shows
// whether it runs the deployed code.
"version_metadata": { "binding": "VERSION_METADATA" },
"vars": {
// Operator overlay.
"CLOUDFLARE_ACCOUNT_ID": "00000000000000000000000000000000",
// AI Gateway the modelGateway routes through — REQUIRED for prefixed model
// ids (`anthropic/*`, `deepseek/*`, `openai/*`, `bedrock/*`), which ride the
// gateway universal endpoint / Bedrock forwarder. `openai/*` additionally
// needs unified billing enabled on this gateway (or a stored OpenAI key —
// mutually exclusive). If the gateway has Authenticated Gateway turned on,
// also set the AI_GATEWAY_AUTH_TOKEN secret. Operator overlay.
"AI_GATEWAY_ID": "your-ai-gateway",
"ADMISSION_CAP": "16",
// The agent and release pools admit at most RunSandboxAgent's and
// RunSandboxRelease's max_instances.
"ADMISSION_CAP_AGENT": "8",
"ADMISSION_CAP_RELEASE": "8",
// Opt the human-facing viewer surfaces (incl. the GitHub App install flow)
// out of the Cloudflare Access gate for the initial dogfood. Configure
// ACCESS_AUD and drop this to re-gate them. NOTE: the viewer surfaces still
// need their OWN Access app before flipping this — the deploy app below only
// path-scopes /deploy. access-auth.ts also reads a single ACCESS_AUD, so
// gating both surfaces needs multi-audience support first (issue #2).
"VIEWER_ACCESS_MODE": "token-only",
// Cloudflare Access team domain (issuer) — shared by the viewer + deploy
// gates. The deploy console verifies the Access JWT against this + the AUD
// below, and fetches identity from <issuer>/cdn-cgi/access/get-identity.
// Operator overlay.
"ACCESS_TEAM_DOMAIN": "your-team.cloudflareaccess.com",
// Deploy console (/deploy) Access application AUD. Distinct from the viewer
// ACCESS_AUD. The app's policy allows the org's GitHub team; per-env
// authorization is CONFIG_KV `deploy.env-authz`. See deploy-access.ts.
// Operator overlay.
"DEPLOY_ACCESS_AUD": "your-deploy-access-aud",
// Execution backend. "on" routes every facade-capable run through the
// SUBSTRATE binding below; anything else keeps the container path in this
// config. Flipping it is the cutover — see apps/substrate/specs/adoption-runbook.md.
// Per-run egress rollout position is a SEPARATE decision, in
// apps/dispatcher/src/grant-catalog.ts.
"SUBSTRATE_BACKEND": "off",
// `"off"` stops the container-stop records (apps/dispatcher/src/container-stop.ts)
// being written to R2; the log line is unaffected. Declared here rather than
// left to default so the switch is greppable and diffable — turning it off
// is still a deploy, so what this buys is a reviewed one-line config change
// instead of a code revert.
"CONTAINER_STOP_RECORDS": "on",
},
// The substrate facade (apps/substrate/specs/adr/0003-facade-only-consumption.md).
// `entrypoint` is the consumer identity: the substrate reads no runtime field
// to decide who is calling, so binding to `DispatcherFacade` here — in
// reviewed config — is what makes this Worker "the dispatcher" for admission
// pools, budgets and grant derivation.
//
// The substrate worker deploys FIRST; a `wrangler deploy` of this Worker
// against a missing service fails at bind time rather than at 2am.
"services": [
{
"binding": "SUBSTRATE",
"service": "flare-dispatch-substrate",
"entrypoint": "DispatcherFacade",
},
],
"workflows": [
{
"name": "runs-workflow",
"binding": "RUNS_WORKFLOW",
"class_name": "RunWorkflow",
},
],
// ===========================================================================
// DRAIN TARGET — this block and the three DO classes below it are what the
// stage-2 exit deletes (apps/substrate/specs/platform.md § Adoption plan:
// "no `containers` stanza exists in any wrangler config except the
// substrate's"). It is still here because three runs — `cdp-acceptance`,
// `product-demo`, `self-heal-pr` — call a detached-process / preview-URL
// surface the facade does not serve, so deleting it today would strand them
// rather than move them. `apps/dispatcher/src/grant-catalog.ts` names that
// per run (`facadeGaps`), and apps/substrate/specs/adoption-runbook.md is
// the sequence that removes it.
// ===========================================================================
//
// Two Dockerfiles, one per image shape. `Dockerfile.sandbox` (lean:
// cloudflare/sandbox base + optional build-tools/rust layers) builds the
// browser/agent classes; `Dockerfile.sandbox-demo` adds the bundled
// `demo-agent` layer (a node builder stage + smoke test) and builds
// RunSandbox. The split is deliberate: a lean class build never pulls the
// node builder stage or ships the bundle, and a dispatch that lands on the
// wrong class still fails honestly at exec ("No such file or directory").
//
// `image_build_context: "."` on RunSandbox is REQUIRED, not cosmetic:
// wrangler's container build defaults the context to the Dockerfile's own
// directory (`infra/`), and the demo builder stage COPYs the workspace
// slice (`packages/demo-agent`, `packages/bedrock-sigv4`, root manifests)
// from the context — with the default context those paths don't exist and
// the image build fails at the first COPY. `.` resolves against the
// wrangler config's directory (the repo root); `.dockerignore` keeps the
// transfer lean.
//
// RunSandbox (the offload-test/check executor + the `product-demo` run's
// default acquisition — `sandbox.acquire({})` gets the DEFAULT image
// class) bakes the native toolchain + Rust: webhook-mode runs have no
// setup step to install one, and consumer Rust workspaces with `*-sys`
// crates both need `cc` to link and more memory than standard-2 gives a
// cold `cargo test` of a large dep graph.
"containers": [
{
"class_name": "RunSandbox",
"image": "./infra/Dockerfile.sandbox-demo",
"image_build_context": ".",
"image_vars": { "WITH_BUILD_TOOLS": "true", "WITH_RUST": "true" },
"instance_type": "standard-4",
"max_instances": 16,
},
{
"class_name": "RunSandboxBrowser",
"image": "./infra/Dockerfile.sandbox",
"instance_type": "standard-2",
"max_instances": 16,
},
{
"class_name": "RunSandboxAgent",
"image": "./infra/Dockerfile.sandbox",
"instance_type": "standard-3",
"max_instances": 8,
},
{
"class_name": "RunSandboxRelease",
"image": "./infra/Dockerfile.sandbox-release",
"image_build_context": ".",
"instance_type": "standard-4",
"max_instances": 8,
// Every deploy pushes a new image and rolls every container. A rollout
// replaces an active instance once this grace has passed, and a
// replaced release container loses its build (`SandboxProcessLost`):
// the grace spans the 14400 s build ceiling, the 1800 s collect step
// and 600 s of checkout. A cell started in the grace keeps its image.
"rollout_active_grace_period": 16800,
},
],
"durable_objects": {
"bindings": [
{ "name": "RUNS_SANDBOX", "class_name": "RunSandbox" },
{ "name": "RUNS_SANDBOX_BROWSER", "class_name": "RunSandboxBrowser" },
{ "name": "AGENT_BUDGET", "class_name": "AgentBudget" },
{ "name": "RUNS_SANDBOX_AGENT", "class_name": "RunSandboxAgent" },
{ "name": "RUNS_SANDBOX_RELEASE", "class_name": "RunSandboxRelease" },
],
},
"migrations": [
{ "tag": "v0", "new_sqlite_classes": ["RunSandbox"] },
{ "tag": "v1", "new_sqlite_classes": ["RunSandboxBrowser"] },
{ "tag": "v2", "new_sqlite_classes": ["AgentBudget"] },
{ "tag": "v3", "new_sqlite_classes": ["RunSandboxAgent"] },
{ "tag": "v4", "new_sqlite_classes": ["RunSandboxRelease"] },
],
// Bucket name, database name and id, and namespace ids: operator overlay.
"r2_buckets": [{ "binding": "RUNS_STORAGE", "bucket_name": "your-runs-bucket" }],
"d1_databases": [
{
"binding": "RUNS_METADATA",
"database_name": "your-runs-database",
"database_id": "00000000-0000-0000-0000-000000000000",
"migrations_dir": "infra/migrations",
},
],
"kv_namespaces": [
{
"binding": "CONFIG_KV",
"id": "00000000000000000000000000000001",
},
{
"binding": "IDEMPOTENCY_KV",
"id": "00000000000000000000000000000002",
},
],
}