Skip to content

Persist functional WordPress state in Cloudflare#1886

Merged
chubes4 merged 92 commits into
mainfrom
fix/1838-cloudflare-bounded-browser
Jul 21, 2026
Merged

Persist functional WordPress state in Cloudflare#1886
chubes4 merged 92 commits into
mainfrom
fix/1838-cloudflare-bounded-browser

Conversation

@chubes4

@chubes4 chubes4 commented Jul 19, 2026

Copy link
Copy Markdown
Collaborator

Summary

Run a functional WordPress site in a Cloudflare Worker with PHP-WASM in the entry Worker, a lightweight Durable Object lease/CAS coordinator, canonical Markdown/JSON state in R2, and disposable reconstructed SQLite.

The runtime supports WordPress login, wp-admin, the block editor, authenticated publishing and media upload, public rendering, complete core block styles, browser assets, serialized concurrent mutations, cold reconstruction, and revision-addressed public page caching.

What changed

  • serialize canonical WordPress mutations through WordPressStateCoordinator
  • bootstrap and persist complete canonical MDI revisions in R2 while keeping SQLite disposable
  • materialize a bounded 2,108-file, 22,960,303-byte server corpus from WordPress 7.0.2
  • package the deterministic runtime corpus as a content-addressed R2 artifact
  • package the complete 2,189-file, 63,646,828-byte WordPress browser surface as one immutable R2 blob with a validated path-to-byte-range manifest
  • package the pinned SQLite integration ZIP as a generic content-addressed runtime archive and load it from R2 during boot
  • serve core, admin, and bundled-theme assets with one exact R2 range read, per-file integrity validation, and artifact-versioned immutable edge caching
  • remove request-time WordPress.org and GitHub release downloads from normal and diagnostic Worker paths
  • retain pointer-matched read runtimes while discarding runtimes after writes and failures
  • cache anonymous HTML by canonical revision before acquiring a mutation lease
  • persist rendered revision snapshots in R2 so cache hits survive Worker replacement and cross POPs
  • persist validated, content-addressed WordPress uploads in R2 and serve them directly without PHP
  • persist bounded plugins, themes, language packs, MU plugins, modified bundled files, and deletion tombstones in the same canonical revision
  • serve canonical public plugin/theme assets directly from revision-addressed R2 before immutable release fallback
  • provide token-protected operator reset and validated canonical pointer restore routes
  • pin merged MDI PR Strengthen apply-back evidence fixture #139 and apply the temporary built overlay from WordPress Playground #4110 until it reaches a published package

Exact-head verification

Verified and deployed at 16d87b5dfa574505be7aaaf86167a9da4469542e:

  • npm run test:cloudflare-runtime: 22 passed, including bounded wp-content/tombstone validation, runtime archive integrity, static artifact range validation, upload security boundaries, and TypeScript
  • npm run test:cloudflare-wordpress-archive-corpus: passed against the real WordPress archive
  • npm run cloudflare:dry-run: passed; 23,101.17 KiB / gzip 8,335.06 KiB
  • npm run cloudflare:local-gate: passed under Wrangler/workerd
  • local canonical full-boot probe: 415 ms
  • local explanatory homepage: 896 ms initial render, 4 ms cache hit

The full local gate proves the canonical MDI full-boot path, explanatory homepage, complete block styles, revision page cache, login, authenticated dashboard, real block editor, two serialized concurrent canonical mutations, authenticated REST publication and media upload, a real plugin ZIP installation and activation, direct R2 plugin CSS delivery, bundled-theme deletion tombstones, byte-exact direct R2 media delivery, exact static R2 range delivery and GET/HEAD semantics, public post rendering, Worker stop/start, persisted post, attachment, plugin code, plugin asset, deletion, and authenticated-session recovery.

The exact head is deployed at https://wp-codebox-cloudflare-runtime.chubes.workers.dev/ through the immutable Homeboy Cloudflare deployment contract. Deployment e4407b8d-b5c3-40d6-8a15-62e3a132aae6 selects Worker version 8133912e-0d7c-4b93-b151-6ee04cd34691. Its same-revision durability redeploy passed the homepage in 810 ms and the representative static asset in 76 ms after runtime replacement. Three production canonical full-boot probes completed in 1.41–1.63 seconds. Production headers confirm x-wp-codebox-static: r2-range, an immutable one-year cache policy, exact content length, and a byte-derived SHA-256 ETag.

Architecture

Browser -> Cloudflare Worker (PHP-WASM + WordPress)
                    |             |
                    |             +-> R2 ranged static corpus + edge cache
                    |             +-> R2 runtime archives (WordPress + SQLite)
                    |
                    +-> pre-runtime revision page cache (edge + R2)
                    |
                    +-> Durable Object (lease + pointer CAS only)
                                      |
                                      +-> R2 canonical MDI revisions
                                          + wp-content + uploads + tombstones
                                          + disposable SQLite reconstruction

Compatibility

Cloudflare runtime state changes from isolate-local mutable state to serialized canonical MDI revisions in R2. Existing health, state, synthetic mutation, and bounded boot-probe routes remain. Token-protected operator reset and restore routes are additive. No public package API is removed.

Source relationships

AI assistance

  • AI assistance: Yes
  • Tool: OpenCode
  • Model: openai/gpt-5.6-sol
  • Used for: diagnosis, implementation, Cloudflare runtime verification, merge reconciliation, deployment, and evidence capture

@chubes4

chubes4 commented Jul 19, 2026

Copy link
Copy Markdown
Collaborator Author

Cloudflare deployment orchestration is tracked in Extra-Chill/homeboy-extensions#2303. This PR is the first consumer: exact-revision deploy, login/publish/assets gate, same-revision redeploy, and MDI/R2 reconstruction proof.

@chubes4

chubes4 commented Jul 19, 2026

Copy link
Copy Markdown
Collaborator Author

Remote deployment evidence for exact commit 2dac41a06e68a43eab871c5d8f1f30766d6c9197:

  • Worker versions 16f64334-fa72-436c-b2a9-bfb83fed0df6 and 4602797d-b35e-467d-beb9-1eb62a87da3b deployed successfully.
  • The prior canonical pointer was backed up byte-for-byte to sites/default/markdown/backups/20260719T034654Z-pre-1886-current.json; only current.json was reset.
  • Fresh /wp-login.php bootstrap failed with Cloudflare 1101. Tail evidence: WordPressStateCoordinator outcome exceededMemory, exception Durable Object's isolate exceeded its memory limit and was reset, CPU 219650 ms, wall 237465 ms.
  • No new current.json was promoted.

Root cause: first boot converts the SQLite seed to canonical MDI files and then starts the canonical runtime inside one Durable Object isolate. The fix is to package a prebuilt canonical MDI seed so cold bootstrap creates only one PHP-WASM runtime from canonical files; SQLite remains disposable. Remote durability claims remain blocked until login/publish/redeploy passes.

@chubes4

chubes4 commented Jul 19, 2026

Copy link
Copy Markdown
Collaborator Author

Follow-up remote evidence for exact commit 7159cf0e72db3b674ca745cc0e0e604df1b6cd6f:

  • Deployed Worker version 4d6fe439-62b2-428e-b4dd-7085f4b79063.
  • Prebuilt canonical MDI seed removed runtime SQLite-to-MDI conversion and reduced failed bootstrap from 219650 ms CPU to 48406 ms CPU.
  • /wp-login.php still returned Cloudflare 1101. Tail: WordPressStateCoordinator outcome exceededMemory, exception Durable Object's isolate exceeded its memory limit and was reset, wall 53293 ms. No canonical pointer was promoted.

The remaining architectural issue is hosting PHP-WASM inside the Durable Object. The next correction keeps PHP-WASM in the entry Worker and restores the DO to a lightweight global lease/current-pointer CAS coordinator. Canonical MDI/R2 state and the prebuilt seed remain unchanged.

@chubes4

chubes4 commented Jul 19, 2026

Copy link
Copy Markdown
Collaborator Author

Remote evidence for exact commit c590c4af3f15d3d6a0c342c878855b4e7ce21652:

  • Deployed Worker version 5f560b41-52bd-4589-82cd-e65ae4ddba11.
  • PHP-WASM is no longer in the Durable Object; the DO remains a lightweight lease/CAS coordinator.
  • Entry Worker bootstrap still returned HTTP 503. Tail: outcome exceededMemory, exception Worker exceeded memory limit, CPU 36740 ms, wall 42410 ms.

The remaining memory source is the selected WordPress filesystem: the predicate currently includes every file under wp-admin/ plus duplicate unminified/minified browser assets. The next correction bounds the production corpus to PHP and production/minified assets required by login, dashboard, editor, and frontend routes while preserving filtered Range extraction.

@chubes4

chubes4 commented Jul 19, 2026

Copy link
Copy Markdown
Collaborator Author

Remote evidence for exact commit 8138eb144f68922e8426ba3d3d760133eb1f3c41:

  • Deployed Worker version 37092595-5ee5-48e9-9d7b-eb42345bec2b.
  • The production corpus dropped from 85.4 MB to 53.7 MB, but /wp-login.php still returned HTTP 503 / Cloudflare 1102. Tail: entry Worker exceededMemory, CPU 67340 ms, wall 74018 ms.

MEMFS overhead plus PHP-WASM still makes 53.7 MB too large. The next boundary keeps only server-executed PHP/templates/metadata in PHP MEMFS and serves individual static CSS/JS/font/image requests by exact bounded Range extraction from the WordPress archive. WordPress script concatenation will be disabled so browser assets do not need to live in PHP memory.

@chubes4

chubes4 commented Jul 19, 2026

Copy link
Copy Markdown
Collaborator Author

Remote evidence for exact commit 36a6c1e5dd76c961760c34b8519f53453ca9e8d8 / version 8f8f7ae3-a171-4e57-a23d-27fa59f08d25:

  • The reduced 22.6 MB PHP corpus removed the prior memory exception.
  • /wp-login.php produced no response within 300 seconds; a second bounded request timed out at 90 seconds.
  • Version-filtered error tail had no completed event/exception.
  • ?phase=r2-state remained responsive with pointer null, version 0; direct R2 confirms no current.json.

The boot-critical dependency is now external multi-range assembly of 1,810 WordPress files. The correction moves the filtered server corpus into a deterministic deployment artifact stored in R2. Worker boot will stream one R2 object into MEMFS; browser assets remain lazy exact-range/cache requests.

@chubes4

chubes4 commented Jul 20, 2026

Copy link
Copy Markdown
Collaborator Author

Final verification update

The diagnostic-free head 486bdf83 passes:

  • npm run test:cloudflare-runtime: 17/17
  • Cloudflare runtime TypeScript check
  • npm run cloudflare:dry-run
  • npm run test:runtime-php-snippets
  • deployed login, dashboard, real block editor, REST publish, public render, and unchanged-revision cold recovery

Two independent infrastructure/tooling gaps remain tracked:

No diagnostic routes or R2 stage artifacts from the investigation remain in the final Worker.

@chubes4

chubes4 commented Jul 21, 2026

Copy link
Copy Markdown
Collaborator Author

Dev-rig and corpus repair pushed in f1ec68bb.

What changed

  • replaced the broken multi-range corpus generation path with one complete archive download plus local decodeZip() selection
  • strengthened the production archive contract against the real WordPress ZIP (4,401 entries, 1,810 selected files, 22,598,313 selected bytes)
  • made workerd readiness parsing ANSI-safe
  • added the exact built overlay from WordPress Playground #4110 at afea9c042999c65f5b43bc049ca4b2bea9d8b361; the patch is removable once a published package contains that change

Verification

  • npm run test:cloudflare-runtime: 18 passed
  • npm run test:cloudflare-wordpress-archive-corpus: passed against the real archive
  • npm run test:runtime-php-snippets: passed
  • npm run cloudflare:dry-run: passed, 22574.94 KiB / gzip: 8212.79 KiB
  • full kimaki tunnel --port 8792 -- npm run cloudflare:local-gate: passed corpus provisioning, concurrent mutations, dashboard/editor, publishing/public rendering, browser assets, cold restart, and MDI reconstruction
  • git diff --check: passed

No production deployment was performed.

…ounded-browser

# Conflicts:
#	package-lock.json
@chubes4

chubes4 commented Jul 21, 2026

Copy link
Copy Markdown
Collaborator Author

Production deployment evidence

Exact head 05b9ba1930de9d3c6348ca9ee139b138c37046af is live at https://wp-codebox-cloudflare-runtime.chubes.workers.dev/.

  • provisioned immutable WordPress corpus runtime/wordpress/6fb95a94d5c06de7ddf35aba94cfc663d281ea500488e5c1206ec374f67a2173.zip to production R2
  • deployed through the tracked Homeboy cloudflare-workers rollback contract
  • final deployment: 87407afb-86d6-45bb-9263-521c167e76f1
  • final version: 9e906844-42d6-48f4-a60b-d9e9fcc65206 at 100% traffic
  • health, login form, and prior persisted post passed after deployment and same-revision redeploy
  • authenticated login redirected 302; dashboard and block editor returned 200
  • authenticated REST publication returned 201
  • new public post returned 200; representative CSS and JavaScript passed through the bounded archive path
  • the new post survived two additional exact-revision deployments; health, login, and post gates returned 200 on the final version

One login-form request during cold reconstruction returned a transient 500; the declared bounded retry succeeded with 200. A parallel post-deploy probe also encountered lease contention, then serial login, dashboard, editor, and public-post verification all returned their expected success statuses. No rollback was required.

@chubes4
chubes4 merged commit 32a7e8c into main Jul 21, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add a Cloudflare-hosted Playground runtime backend

1 participant