Skip to content

Commit 2be2d25

Browse files
committed
Disable browser-request cron scheduling
1 parent 7d439cb commit 2be2d25

4 files changed

Lines changed: 17 additions & 13 deletions

File tree

packages/runtime-cloudflare/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ The bundled MDI source is pinned to immutable commit `1870fb41279e7eb5946e506c9c
1818

1919
The WordPress server corpus is a separate deployment artifact, never a Worker-module import. `generate:cloudflare-wordpress-runtime-corpus` uses bounded ZIP Range decoding against the pinned WordPress release, retains only `isWordPressRuntimeFile()` entries, and emits a deterministic ZIP under `artifacts/` plus the checked-in `assets/wordpress-runtime-artifact.json` provenance manifest. The immutable R2 key is derived from the corpus ZIP SHA-256. Cold boot fetches that exact object through `WORDPRESS_STATE_BUCKET`, validates the manifest and archive/file hashes and budgets, then streams it into PHP MEMFS. Static browser assets continue to use lazy exact archive ranges and Worker cache.
2020

21-
Canonical Cloudflare boots patch only the assembled PHP MEMFS copy of `/wordpress/wp-settings.php` after the R2 WordPress corpus is materialized and before WordPress executes. The patch requires exactly one canonical `do_action( 'init' );` needle, fails closed otherwise, removes only core's `wp_cron` callback through public `remove_action()` when `DISABLE_WP_CRON` is true, then executes the original `init` call once. Cron spawning is explicitly disabled by this runtime policy because the PHP-WASM/MDI shutdown callback path is not supported. Update and privacy scheduling callbacks remain registered; an explicit durable cron execution contract is separate future scope.
21+
Canonical Cloudflare boots patch only the assembled PHP MEMFS copy of `/wordpress/wp-settings.php` after the R2 WordPress corpus is materialized and before WordPress executes. The disabled-cron scheduling policy requires exactly one canonical `do_action( 'init' );` needle and fails closed otherwise. When `DISABLE_WP_CRON` is true, it removes only core's `wp_cron`, `wp_schedule_delete_old_privacy_export_files`, and `wp_schedule_update_checks` callbacks through public `remove_action()` before executing the original `init` call once. This prevents browser requests from recreating cron work that cannot execute in the PHP-WASM/MDI shutdown path. Durable cron execution belongs to a separate explicit queue or scheduled-Worker contract.
2222

2323
## MDI Init Diagnostics
2424

packages/runtime-cloudflare/src/worker.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1003,7 +1003,7 @@ async function bootWordPressRuntime(
10031003
siteUrl = SITE_URL,
10041004
authConstants: Partial<Record<WordPressAuthConstant, string>> = {},
10051005
runtimeBucket?: R2Bucket,
1006-
shouldPatchCanonicalCronAtInit = false,
1006+
shouldPatchCanonicalDisabledCronSchedulingPolicyAtInit = false,
10071007
): Promise<{ php: PHP; requestHandler: PHPRequestHandler; wordpressVersion: string }> {
10081008
const requestHandler = await bootWordPressAndRequestHandler({
10091009
createPhpRuntime,
@@ -1035,7 +1035,7 @@ async function bootWordPressRuntime(
10351035
materializeRuntimeFiles(php, MARKDOWN_ROOT, markdownFiles)
10361036
if (markdownIndexSeed) php.writeFile(MARKDOWN_RESOLVED_INDEX_PATH, markdownIndexSeed)
10371037
}
1038-
if (shouldPatchCanonicalCronAtInit) patchCanonicalCronAtInit(php)
1038+
if (shouldPatchCanonicalDisabledCronSchedulingPolicyAtInit) patchCanonicalDisabledCronSchedulingPolicyAtInit(php)
10391039
} : undefined,
10401040
beforeDatabaseSetup: databaseSeed ? (php: PHP) => {
10411041
php.mkdir("/wordpress/wp-content/database")
@@ -1063,16 +1063,18 @@ function materializeRuntimeFiles(php: PHP, root: string, files: RuntimeFile[]):
10631063
}
10641064
}
10651065

1066-
function patchCanonicalCronAtInit(php: PHP): void {
1066+
function patchCanonicalDisabledCronSchedulingPolicyAtInit(php: PHP): void {
10671067
const settingsPath = "/wordpress/wp-settings.php"
10681068
const needle = "do_action( 'init' );"
10691069
const settings = new TextDecoder().decode(php.readFileAsBuffer(settingsPath))
10701070
const firstNeedle = settings.indexOf(needle)
10711071
if (firstNeedle === -1 || firstNeedle !== settings.lastIndexOf(needle)) {
1072-
throw new Error("WordPress canonical cron patch needle was not uniquely found.")
1072+
throw new Error("WordPress canonical disabled-cron scheduling policy patch needle was not uniquely found.")
10731073
}
10741074
const replacement = `if ( defined( 'DISABLE_WP_CRON' ) && DISABLE_WP_CRON ) {
10751075
remove_action( 'init', 'wp_cron' );
1076+
remove_action( 'init', 'wp_schedule_delete_old_privacy_export_files' );
1077+
remove_action( 'init', 'wp_schedule_update_checks' );
10761078
}
10771079
${needle}`
10781080
php.writeFile(settingsPath, new TextEncoder().encode(`${settings.slice(0, firstNeedle)}${replacement}${settings.slice(firstNeedle + needle.length)}`))

scripts/cloudflare-local-gate.mjs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -172,14 +172,14 @@ async function assertCanonicalProbes() {
172172
const wordpress = await (await request(`${origin}/?phase=canonical-wordpress`)).json()
173173
const setup = await (await request(`${origin}/?phase=canonical-bootstrap-setup`)).json()
174174
const counts = ["canonicalSeedFiles", "postCount", "pageCount", "userCount", "optionCount", "widgetOptionCount", "widgetStateOptionCount", "memoryBytes", "peakMemoryBytes"]
175-
if (wordpress.completed !== true || typeof wordpress.evidence?.wordpressVersion !== "string" || counts.some((key) => !Number.isInteger(wordpress.evidence?.[key])) || wordpress.evidence.widgetStateOptionCount !== 19 || wordpress.evidence.wpCronInitAttached !== false || wordpress.evidence.updateScheduleInitAttached !== true || wordpress.evidence.privacyScheduleInitAttached !== true) {
175+
if (wordpress.completed !== true || typeof wordpress.evidence?.wordpressVersion !== "string" || counts.some((key) => !Number.isInteger(wordpress.evidence?.[key])) || wordpress.evidence.widgetStateOptionCount !== 19 || wordpress.evidence.wpCronInitAttached !== false || wordpress.evidence.updateScheduleInitAttached !== false || wordpress.evidence.privacyScheduleInitAttached !== false) {
176176
throw new Error(`Canonical WordPress probe did not boot the complete seed: ${JSON.stringify(wordpress)}`)
177177
}
178178
const changedCounts = ["createdPathCount", "changedPathCount", "deletedPathCount"]
179179
if (setup.completed !== true || typeof setup.evidence?.wordpressVersion !== "string" || changedCounts.some((key) => !Number.isInteger(setup.evidence?.[key])) || setup.evidence.changedPathCount < 1) {
180180
throw new Error(`Canonical bootstrap setup probe did not produce ephemeral changes: ${JSON.stringify(setup)}`)
181181
}
182-
console.log(`Canonical probes: WordPress ${wordpress.evidence.wordpressVersion}, cron init disabled while update/privacy scheduling remain registered, ${wordpress.evidence.widgetOptionCount} widget_* options plus sidebars_widgets, ${setup.evidence.changedPathCount} changed ephemeral paths.`)
182+
console.log(`Canonical probes: WordPress ${wordpress.evidence.wordpressVersion}, disabled-cron scheduling callbacks absent at init, ${wordpress.evidence.widgetOptionCount} widget_* options plus sidebars_widgets, ${setup.evidence.changedPathCount} changed ephemeral paths.`)
183183
}
184184

185185
async function assertMdiDiagnostics() {

tests/cloudflare-runtime.test.ts

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -313,22 +313,24 @@ test("Cloudflare MDI lifecycle diagnostics use the complete packaged seed withou
313313
assert.match(worker, /function canonicalChangedPathCounts/)
314314
})
315315

316-
test("Cloudflare canonical runtime patches the unique init call in MEMFS without removing other scheduling callbacks", async () => {
316+
test("Cloudflare canonical runtime patches the unique init call with the disabled-cron scheduling policy", async () => {
317317
const worker = await readFile(new URL("../packages/runtime-cloudflare/src/worker.ts", import.meta.url), "utf8")
318-
const patcher = worker.slice(worker.indexOf("function patchCanonicalCronAtInit"), worker.indexOf("\nfunction collectRuntimeFiles"))
318+
const patcher = worker.slice(worker.indexOf("function patchCanonicalDisabledCronSchedulingPolicyAtInit"), worker.indexOf("\nfunction collectRuntimeFiles"))
319319

320-
assert.ok(patcher.startsWith("function patchCanonicalCronAtInit"), "The canonical cron init patcher is present.")
320+
assert.ok(patcher.startsWith("function patchCanonicalDisabledCronSchedulingPolicyAtInit"), "The canonical disabled-cron scheduling policy patcher is present.")
321321
assert.match(patcher, /const settingsPath = "\/wordpress\/wp-settings\.php"/)
322322
assert.match(patcher, /php\.readFileAsBuffer\(settingsPath\)/)
323323
assert.match(patcher, /firstNeedle === -1 \|\| firstNeedle !== settings\.lastIndexOf\(needle\)/)
324-
assert.match(patcher, /throw new Error\("WordPress canonical cron patch needle was not uniquely found\."\)/)
324+
assert.match(patcher, /throw new Error\("WordPress canonical disabled-cron scheduling policy patch needle was not uniquely found\."\)/)
325325
assert.match(patcher, /defined\( 'DISABLE_WP_CRON' \) && DISABLE_WP_CRON/)
326326
assert.match(patcher, /remove_action\( 'init', 'wp_cron' \)/)
327+
assert.match(patcher, /remove_action\( 'init', 'wp_schedule_delete_old_privacy_export_files' \)/)
328+
assert.match(patcher, /remove_action\( 'init', 'wp_schedule_update_checks' \)/)
327329
assert.equal((patcher.match(/do_action\( 'init' \);/g) ?? []).length, 1)
328330
assert.match(patcher, /php\.writeFile\(settingsPath/)
329-
assert.doesNotMatch(patcher, /mu-plugins|add_action\(|wp_schedule_update_checks|wp_schedule_delete_old_privacy_export_files|\$GLOBALS\['wp_filter'\]/)
331+
assert.doesNotMatch(patcher, /mu-plugins|add_action\(|WP_Site_Health|wp_schedule_site_health_cron|\$GLOBALS\['wp_filter'\]/)
330332
assert.doesNotMatch(worker, /materializeCanonicalCronAdapter|wp-codebox-canonical-cron-policy/)
331-
assert.match(worker, /runtimeBucket\?: R2Bucket,\n shouldPatchCanonicalCronAtInit = false,/)
333+
assert.match(worker, /runtimeBucket\?: R2Bucket,\n shouldPatchCanonicalDisabledCronSchedulingPolicyAtInit = false,/)
332334
assert.match(worker, /authConstants, bucket, true\), pointer/)
333335
assert.match(worker, /env\.WORDPRESS_STATE_BUCKET, true\)/)
334336
assert.match(worker, /canonical-probe\.invalid", \{\}, bucket, true\)/)

0 commit comments

Comments
 (0)