Skip to content

[wasm-posix-kernel] WordPress Playground CLI under wasm-posix-kernel - Proof of concept#3604

Closed
mho22 wants to merge 15 commits into
WordPress:trunkfrom
mho22:playground-cli-experimental-posix-kernel
Closed

[wasm-posix-kernel] WordPress Playground CLI under wasm-posix-kernel - Proof of concept#3604
mho22 wants to merge 15 commits into
WordPress:trunkfrom
mho22:playground-cli-experimental-posix-kernel

Conversation

@mho22
Copy link
Copy Markdown
Collaborator

@mho22 mho22 commented May 6, 2026

Summary

Adds an experimental --experimental-posix-kernel flag to the
Playground CLI that boots WordPress under nginx + PHP-FPM running on
wasm-posix-kernel,
instead of the existing PHP.wasm Asyncify/JSPI runtime.

This PR is for visibility / posterity. It is not ready to
merge — it depends on five kernel-side fixes that have not yet
been upstreamed. The staging PR for those fixes is at
mho22/wasm-posix-kernel#50.

What this adds

A new --experimental-posix-kernel CLI flag that:

  1. Loads the wasm-posix-kernel host bridge from a user-provided
    WASM_POSIX_KERNEL_DIR checkout.
  2. Boots nginx + PHP-FPM inside the kernel, with a FastCGI router
    that serves static files and routes WordPress requests through
    index.php (no PCRE in the nginx build).
  3. Auto-prepares WordPress (downloads WP + SQLite Database
    Integration when no --mount is given) and runs the install
    over HTTP POST against the in-kernel server.
  4. Implements a KernelLimitedPHPApi shim so existing v1
    blueprint steps (runPHP, installPlugin, writeFile,
    setSiteOptions, login, etc.) work against the
    kernel-resident WordPress.
  5. Clears stale playground_auto_login_already_happened cookies
    on the first request via a filesystem marker (avoids a 302
    loop after --login runs).

Demo:

WASM_POSIX_KERNEL_DIR=/path/to/wasm-posix-kernel \
  npx nx dev playground-cli server \
    --experimental-posix-kernel --wp=latest --login

Commits

  1. b1f102a Experimental posix-kernel: nginx + PHP-FPM boot pipeline
  2. 9b7c2e6 Experimental posix-kernel: KernelLimitedPHPApi shim
  3. 7e8665c Experimental posix-kernel: WordPress preparation
  4. 1834dd4 Experimental posix-kernel: handler + --experimental-posix-kernel CLI flag
  5. 628bdfa Experimental posix-kernel: kernel-mode test suite
  6. d8afabe posix-kernel: clear stale auto-login cookie via first-request marker
  7. aa56dbd posix-kernel: require WASM_POSIX_KERNEL_DIR env var
  8. 38fb9cf posix-kernel: quiet nginx error_log + disable access_log

Why this is experimental

  • Depends on five fixes in wasm-posix-kernel that are not yet
    upstreamed (see mho22/wasm-posix-kernel#50):
    • PHP-FPM stack-size fix (fcgi_read_request buf[65543]
      overflows BSS into musl's vmlock, causing the install POST
      to deadlock on __vm_wait).
    • Kernel wakeBlockedPoll snapshot fix (live Map iteration
      livelocked the worker under nginx + PHP-FPM I/O patterns).
    • Host chown EPERM/EINVAL/ENOTSUP swallowing (kernel runs every
      process as uid 0; unprivileged host FS can't honor chowns).
    • Host node-kernel-worker-entry bundle fix (worker entry
      wasn't emitted by tsup, so consumers couldn't require.resolve
      it from the published dist/).
    • PHP wasm size optimization (-Oz + DWARF strip; ~28% smaller).
  • Tests require a built wasm-posix-kernel checkout with
    prebuilt PHP / nginx wasm artifacts. Those binaries take
    30-60 minutes to build and need Docker + Emscripten. Until
    kernel-side publishes release artifacts, these tests can only
    run on developer machines that have the sibling checkout —
    they will not run in this repo's CI as-is.
  • Production CLI bundle does not yet copy-files the runtime
    resources
    (configs/nginx.conf, configs/php-fpm.conf,
    router.php, wp-templates/). The dev path resolves them
    directly from src/. See "Open follow-ups".

Open follow-ups (deliberately out of scope)

  1. Bundle copy-files for runtime resources. The production
    vite build needs to ship posix-kernel/configs/, router.php,
    and wp-templates/ into dist/.
  2. --site-url support under kernel mode. The kernel handler
    currently ignores args['site-url']; wp-templates/wp-config.php
    derives WP_HOME / WP_SITEURL from the request Host header.
  3. Intl extension. Surface intl.so + icudt74l.dat into the
    kernel filesystem and wire extension=intl.
  4. Replace the env var with an npm-installable kernel package.
    Once wasm-posix-kernel publishes prebuilt artifacts +
    host bundle to npm, host-bridge.ts can drop the
    WASM_POSIX_KERNEL_DIR indirection and switch to a regular
    package import.

Test plan

The 16 kernel-mode tests in
packages/playground/cli/tests/posix-kernel/*.spec.ts pass locally
against a built wasm-posix-kernel checkout (HEAD of
mho22:playground-cli-experimental-posix-kernel):

WASM_POSIX_KERNEL_DIR=/path/to/wasm-posix-kernel \
  npx nx test-playground-cli playground-cli \
    --testFile=tests/posix-kernel/auto-prepare.spec.ts

They will fail in stock CI without a kernel checkout — expected
at this stage. See "Why this is experimental".

🤖 Generated with Claude Code

@mho22 mho22 requested review from a team, JanJakes and Copilot May 6, 2026 20:35
@mho22 mho22 removed the request for review from JanJakes May 6, 2026 20:36
@mho22 mho22 marked this pull request as draft May 6, 2026 20:37
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Note

Copilot was unable to run its full agentic suite in this review.

Adds an experimental --experimental-posix-kernel mode for Playground CLI that boots WordPress under wasm-posix-kernel (nginx + PHP-FPM) and provides a KernelLimitedPHPApi shim so Blueprint v1 steps can run against the kernel-hosted site.

Changes:

  • Introduces a new CLI flag and execution path that bypasses Express + PHP.wasm worker pool in favor of wasm-posix-kernel.
  • Adds kernel-mode boot pipeline, host-bridge resolution, WordPress auto-prepare/install helpers, and PHP/HTTP shim API.
  • Adds a dedicated vitest suite for kernel mode (boot, CLI behavior, blueprint v1 smoke, stdout-capture regression).

Reviewed changes

Copilot reviewed 19 out of 19 changed files in this pull request and generated 12 comments.

Show a summary per file
File Description
packages/playground/cli/src/run-cli.ts Adds --experimental-posix-kernel flag and routes execution to PosixKernelHandler.
packages/playground/cli/src/start-server.ts Adds reserveFreePort() for kernel mode port selection.
packages/playground/cli/src/posix-kernel/boot.ts Boots wasm-posix-kernel, spawns nginx + PHP-FPM, provides spawn-capture runtime.
packages/playground/cli/src/posix-kernel/host-bridge.ts Resolves kernel checkout via WASM_POSIX_KERNEL_DIR, loads host bundle + wasm binaries.
packages/playground/cli/src/posix-kernel/posix-kernel-handler.ts Orchestrates kernel boot, WordPress prepare/install, and Blueprint v1 execution.
packages/playground/cli/src/posix-kernel/prepare-wordpress.ts Downloads/extracts WordPress + SQLite integration and drives the installer via HTTP.
packages/playground/cli/src/posix-kernel/php-api.ts Implements KernelLimitedPHPApi shim (fs ops, HTTP request, php -r execution, defines store).
packages/playground/cli/src/posix-kernel/configs/nginx.conf Nginx config template that routes all requests through router.php.
packages/playground/cli/src/posix-kernel/configs/php-fpm.conf PHP-FPM config tuned for kernel constraints and install-time deadlock avoidance.
packages/playground/cli/src/posix-kernel/router.php FastCGI router that serves static files, includes PHP, or routes to WordPress index.php.
packages/playground/cli/src/posix-kernel/wp-templates/wp-config.php Kernel-mode wp-config template (SQLite + debug constants + Host-derived site URL).
packages/playground/cli/src/posix-kernel/wp-templates/playground-defines.php Mu-plugin template that reads JSON store and defines constants per request.
packages/playground/cli/src/posix-kernel/wp-templates/auto-login.php Mu-plugin template providing auto-login behavior.
packages/playground/cli/src/posix-kernel/wp-templates/disable-wp-mail.php Mu-plugin template that no-ops wp_mail() to avoid sendmail execution.
packages/playground/cli/tests/posix-kernel/*.spec.ts Adds kernel-mode vitest coverage for boot, CLI behaviors, blueprint v1, auto-prepare, and stdout capture.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread packages/playground/cli/src/posix-kernel/router.php
Comment thread packages/playground/cli/src/posix-kernel/router.php
Comment thread packages/playground/cli/src/posix-kernel/configs/nginx.conf Outdated
Comment thread packages/playground/cli/src/posix-kernel/wp-templates/wp-config.php
Comment thread packages/playground/cli/src/posix-kernel/wp-templates/wp-config.php
Comment thread packages/playground/cli/src/posix-kernel/posix-kernel-handler.ts
Comment thread packages/playground/cli/src/posix-kernel/posix-kernel-handler.ts Outdated
Comment thread packages/playground/cli/src/posix-kernel/wp-templates/wp-config.php Outdated
Comment thread packages/playground/cli/src/run-cli.ts
Comment thread packages/playground/cli/tests/posix-kernel/boot.spec.ts
mho22 and others added 15 commits May 12, 2026 16:41
Adds the standalone boot pipeline for an experimental
--experimental-posix-kernel mode. nginx + PHP-FPM run inside a
sibling wasm-posix-kernel checkout (located via the
WASM_POSIX_KERNEL_DIR env var); the host bridge dynamic-imports
the kernel's host/dist bundle so we don't depend on it as an npm
package. boot.ts reserves a numeric port (kernel-resident nginx
can't take :0), spawns php-fpm and nginx, multiplexes their
stdout/stderr, and exposes a KernelRuntime that lets later layers
spawn additional php.wasm CLIs against the same host. The
single-FastCGI-entry router.php is loaded in-place by nginx and
covers the static / PHP-on-disk / WordPress-fallback split, since
the kernel's nginx is built without PCRE.

No CLI wiring yet — slice 1 is just the boot primitives.
Adds the LimitedPHPApi-shaped surface that blueprint-v1 step
implementations expect, against the kernel-resident WordPress
install. Filesystem methods read/write the host filesystem
directly (kernel uses host fs through wasm-posix-kernel's
NodePlatformIO, so the bytes are the same). request() goes to
nginx via fetch with an in-memory cookie jar; run({ code }) /
cli({ argv }) spawn fresh php.wasm CLIs against the same kernel
host through KernelRuntime.spawnCapturing().

defineConstant() persists a JSON store and regenerates a
playground-defines.php mu-plugin so the constants survive
across requests (nginx + FPM are stateless w.r.t. the
playground process). The mu-plugin template lives in
wp-templates/ alongside the other PHP fixtures.

Path translation: the API surface keeps documentRoot as the
host's wordPressRoot (so blueprint step PHP code that does
require_once "{docRoot}/wp-load.php" resolves correctly under
both classic-VFS and kernel modes), and a tightened
VFS_DOCROOT_IN_CODE regex with a (?<![\\w/-]) lookbehind avoids
re-rewriting the trailing /wordpress directory when it appears
inside a host path.
Materializes a self-contained WordPress document root for the
kernel mode. Reuses Playground's existing WP release resolver,
cached download helper, SQLite Database Integration fetch, and
the @php-wasm/stream-compression zip decoder; idempotent each
step skips work that's already on disk.

ensureWordPressInstalled() drives wp-admin/install.php?step=2
over fetch, since a standalone php.wasm bootstrapping wp-load
hangs on the SQLite drop-in's per-request connection setup —
posting through the working FPM pipeline reuses the
nginx + php-fpm stack we already booted.

Three PHP fixtures land in wp-templates/:

- wp-config.php — minimal Playground-flavored config; WP_DEBUG /
  WP_DEBUG_LOG / WP_DEBUG_DISPLAY are guarded with !defined()
  so the playground-defines mu-plugin (set via --define-bool
  flags from the CLI) wins. Without the guard, redefinition
  warnings prepend HTML to JSON test responses.
- disable-wp-mail.php — no-op wp_mail() so wp_install()'s
  wp_new_blog_notification doesn't take the
  PHPMailer→popen→sendmail path the wasm-posix-kernel can't
  resolve. Mu-plugins load before pluggable.php's
  function_exists('wp_mail') guard, so the no-op wins.
- auto-login.php — trimmed copy of the 1-auto-login.php
  mu-plugin generated in @wp-playground/wordpress's boot
  helpers; signs in as PLAYGROUND_AUTO_LOGIN_AS_USER on first
  request.
…flag

Wires the kernel-mode boot pipeline into the CLI. PosixKernelHandler
mirrors BlueprintsV1Handler / BlueprintsV2Handler — bootWordPress()
resolves a port (reserveFreePort if --port is in use), prepares
WordPress when no /wordpress mount is provided, brings up the
kernel-resident nginx + php-fpm via bootPosixKernelWordPress, then
drives the WordPress installer over HTTP. runBlueprint() applies
--define / --define-bool / --define-number constants and runs the
compiled blueprint v1 steps through KernelLimitedPHPApi.

run-cli.ts gets a hidden --experimental-posix-kernel boolean and a
new runCLI overload that returns PosixKernelRunCliServer (just
serverUrl + LimitedPHPApi + dispose; no Express server, no
PHPWorker pool). The --experimental-posix-kernel branch refuses
xdebug / redis / memcached and currently only supports the server
command. start-server.ts gains a reserveFreePort() helper since
kernel-resident nginx needs an explicit numeric port.
Five spec files exercising --experimental-posix-kernel:

- boot.spec.ts — minimal smoke test against a static index.php
  document root, mounted via --mount=<dir>:/wordpress.
- auto-prepare.spec.ts — no --mount: WordPress + SQLite drop-in
  download, install drives end-to-end, GET / returns 200 with
  WordPress markup.
- blueprint-v1.spec.ts — runPHP, writeFile, mkdir, login steps
  against the kernel-resident WordPress; the writeFile output is
  fetched back through nginx as a regression test for path
  translation.
- php-api.spec.ts — drives KernelLimitedPHPApi.run() directly
  (sequential and parallel) to regression-cover the time-multiplexed
  stdout capture in boot.ts (the kernel currently emits every
  stdout chunk with pid: 0, so a misordered capture would corrupt
  blueprint runPHP output).
- run-cli.spec.ts — curated kernel-mode subset of
  tests/run-cli.spec.ts: --define matrix (string/bool/number),
  --wp version selection, default site URL, blueprint with git
  resources, internal cookie store persistence, port-in-use
  fallback, and the auto-login cookie-clear scenario.

Tests bind in serial — the kernel host boots one nginx + php-fpm
per test file, so running the whole tests/posix-kernel/ directory
in parallel is flaky; per-file invocations are reliable.
Mirrors the classic CLI's Express middleware that clears a stale
playground_auto_login_already_happened cookie on the first real
request after boot. The classic path runs in Node; under
--experimental-posix-kernel the front door is the kernel-resident
nginx, so the equivalent has to live in router.php.

boot.ts threads a per-tempDir marker path
(<tempDir>/first-request-pending) into nginx as
fastcgi_param PLAYGROUND_FIRST_REQUEST_MARKER and exposes
resetFirstRequestMarker() on the boot result. The handler arms
the marker post-install (creating it earlier would short-circuit
the install probe in ensureWordPressInstalled). router.php
@Unlink's the marker on the first request that observes it (FPM
workers race; the unlink is atomic, only one wins) and emits a
302 + Set-Cookie clearing the cookie ONLY when the request
actually carries the cookie — an unconditional 302 to
$_SERVER['REQUEST_URI'] would trip undici's redirect-cycle
detection and break tests that issue plain fetches.

Unblocks the auto-login describe in run-cli.spec.ts.
Drop the hardcoded developer-machine default for the kernel checkout
location. The CLI now errors out with a clear message if the env
var is unset, so the path is no longer baked into the source.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The default config flooded stdout with nginx [notice]/[info]
startup chatter and a per-request access line. Drop error_log
to 'error' and turn access_log off so the dev console only shows
the kernel-mode banner, the FPM ready notice, and the
'WordPress is ready at ...' line. PHP-FPM's NOTICE banner is
still shown (useful, low volume). Tests don't depend on log
output (boot uses TCP loopback probing).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The 16 kernel-mode specs under
packages/playground/cli/tests/posix-kernel/ require a built
wasm-posix-kernel checkout (host/dist/index.js + 4 wasm binaries)
that the CLI's host-bridge.ts loads at runtime via
WASM_POSIX_KERNEL_DIR. Building it from source needs Docker +
Emscripten and takes 30-60 minutes — too heavy to run on every CI
job.

Wire the kernel as a git submodule pinned to an artifacts branch on
mho22/wasm-posix-kernel that ships pre-built host/dist/ + the four
wasms (kernel.wasm, nginx.wasm, php.wasm, php-fpm.wasm). The
existing CI workflow already runs actions/checkout@v4 with
'submodules: true', so no workflow change is needed.

Adds tests/posix-kernel/setup.ts as a vitest setupFiles entry. It
sets process.env.WASM_POSIX_KERNEL_DIR to the submodule path on
disk if (and only if) the env var is not already set by the
developer. The fork-pool workers inherit the env var, and
host-bridge.ts then resolves host/dist/index.js + the wasm
binaries from the submodule.

The submodule URL points at mho22/wasm-posix-kernel (a personal
fork) because wasm-posix-kernel does not yet publish release
artifacts to npm or GitHub Releases. Once it does, swap the
submodule for a tarball download in postinstall.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The previous configs capped concurrency at one PHP request at a time
(nginx worker_processes=1, FPM pm=static with pm.max_children=1).
That kneecapped anything that fans out — Site Editor REST calls,
WP-Cron, async block fetches — by serializing every request through
a single FPM worker.

Switch nginx to worker_processes=auto and FPM to pm=dynamic with
pm.max_children=4 (start_servers=2, min_spare=1, max_spare=3). The
kernel-side prerequisite — wasm-posix-kernel commit f7b3f9eb's
shared AF_INET accept queue across fork — is already in the HEAD
this PR depends on, so multi-worker nginx is safe.

Drop the disable_functions=fsockopen + allow_url_fopen=0 hardening
that mirrored boot.ts:578-583. They were a workaround for the WP
installer's wp_install_maybe_enable_pretty_permalinks() self-loopback
deadlocking against pm.max_children=1 (the only worker was already
busy serving the install POST that triggered the loopback). With
>= 2 children the loopback completes naturally and the hardening is
no longer load-bearing.

Memory note: 4 PHP-FPM children + 2 nginx workers ≈ 6 wasm
processes vs. 2 today. Each PHP wasm is ~18 MB, so ~70-80 MB
resident at peak — fine for a dev CLI.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
… apply CLI constants pre-install

Address three Copilot review findings on PR #3604:

1. nginx was listening on 0.0.0.0 by default (no interface in the
   `listen` directive), which exposes the experimental dev server on
   the LAN even though the CLI advertises 127.0.0.1. The configs now
   use a `listen __HOST__:__PORT__` placeholder substituted by
   bootPosixKernelWordPress; the new `host` boot option defaults to
   `'127.0.0.1'` (kept symmetric with the existing `__PORT__`,
   `__SERVER_NAME__`, etc. placeholders so a future `--host` CLI
   flag becomes a one-line plumb-through).

2. wp-templates/wp-config.php defaulted `WP_DEBUG_DISPLAY` to true,
   while the classic CLI defaults it to false (run-cli.ts:723-724).
   The mismatch let PHP notices/warnings prepend HTML to JSON
   responses on early requests. Flipped to false to match.

3. mergeDefinedConstants(this.args) ran inside runBlueprint(), i.e.
   *after* bootWordPress()'s ensureWordPressInstalled() drove the
   WP installer. So `--define WP_DEBUG=...` and friends never made
   it into the install POST. Classic mode applies these via
   bootWordPress() before install; mirror that ordering by
   hoisting the loop into bootWordPress() before the install call.

Drive-bys from the host-placeholder change:
  - Renamed the kernel-runtime variable `host` to `kernelHost`
    inside bootPosixKernelWordPress to free up `host` for the
    network-bind concept (KernelRuntime.host → .kernelHost
    accordingly; not consumed by external callers).
  - waitForLoopback now takes the bind host explicitly. The FPM
    loopback probe still hits 127.0.0.1 (kernel-internal port),
    but the nginx probe matches the user-facing bind address.
  - Dropped the redundant URL from the "Booting WordPress…"
    pre-boot status print; run-cli.ts already emits a final
    "WordPress is ready at <serverUrl>" once boot completes.

All 16 kernel-mode specs pass (153 tests across 13 files in the
playground-cli vitest suite).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…in CI

Two fixes for CI failures on PR #3604:

1. `node:url` was missing from getExternalModules() in
   vite-external-modules.ts. host-bridge.ts imports `pathToFileURL`
   from `node:url` for the dynamic-import URL of the kernel's host
   bundle, so production rollup builds (`nx build playground-cli`,
   `package-for-self-hosting`) failed with `"pathToFileURL" is not
   exported by "__vite-browser-external"`. Adding it alongside the
   existing `node:fs`, `node:crypto`, `node:http`, `node:net`,
   `node:process` entries lets rollup keep the import as external,
   matching how the file is actually consumed (Node-only).

2. The kernel's host bundle (wasm-posix-kernel/host/dist/index.js)
   leaves `fflate` and `fzstd` as external imports. They are
   declared in wasm-posix-kernel/host/package.json's dependencies,
   but the submodule's host/ directory is not part of the playground
   workspaces, so npm ci on the runners doesn't install them. The
   kernel-mode specs failed at `import { inflateSync } from "fflate"`.
   Add a CI step that runs `npm install --omit=dev --no-package-lock
   --ignore-scripts --prefix wasm-posix-kernel/host` before the
   test-playground-cli matrix's test step, populating
   wasm-posix-kernel/host/node_modules/{fflate,fzstd} where Node's
   resolver finds them when the host bundle is dynamic-imported.
The published @wp-playground/cli bundle ships only the JS chunks Vite
emits in dist/ — the wp-templates/*.php, configs/*.conf, and router.php
source files don't end up alongside the runtime. Module-init readFileSync
calls for those resources crashed test-built-npm-packages with ENOENT
because consumers install the published tarball, not the source tree.

Switch every static resource load to a Vite `?raw` import so the file
contents are inlined as string literals at build time:

  - php-api.ts: DEFINES_MU_PLUGIN_PHP
  - prepare-wordpress.ts: WP_CONFIG_PHP, DISABLE_WP_MAIL_MU_PLUGIN_PHP,
    AUTO_LOGIN_MU_PLUGIN_PHP
  - boot.ts: ROUTER_PHP, NGINX_CONF_TEMPLATE, PHP_FPM_CONF

For the boot-time config files, write the inlined strings to tempDir at
boot — nginx and php-fpm need real paths, but those paths no longer have
to live next to the JS bundle. Drop the now-unused __dirname constant
from boot.ts and switch nginx's prefix `cwd` to tempDir.

Add `vite/client` to tsconfig.lib.json + tsconfig.spec.json so tsc
recognizes the `?raw` import suffix.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The shipped kernel.wasm is wasm64 and the host bundle creates a
WebAssembly.Memory descriptor with `address: "i64"`. Node 22's V8
rejects that form ("Cannot convert a BigInt value to a number"),
the worker-thread init throws, and the kernel host hangs forever
on the "ready" message — every --experimental-posix-kernel test
times out. Node 24's V8 accepts memory64 natively.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…play_errors

Two polish fixes surfaced smoke-testing --experimental-posix-kernel
--login in a real browser:

1. router.php only handled file paths. A request like /wp-admin/
   maps to a directory on disk, is_file() returned false, and the
   request fell through to the WP front-end index.php — URL bar
   said /wp-admin/ but the page rendered the homepage. Hitting
   /wp-admin/index.php directly already worked. Added a
   DirectoryIndex branch that resolves <dir>/index.php and includes
   it so the trailing-slash form behaves the same.

2. php-fpm is launched with `-c /dev/null` (no php.ini), so PHP's
   compiled-in display_errors=On default applies under FastCGI.
   That writes E_USER_NOTICE/E_WARNING into the response body
   (e.g. wp_version_check()'s trigger_error() output prepended to
   wp-admin HTML), which then cascades into "Cannot modify header
   information" warnings. Set php_admin_flag[display_errors]=off in
   the [www] pool; errors still flow to stderr via the existing
   global error_log. (display_errors=stderr only works under CLI
   SAPI — under FastCGI it falls back to the body.)

Both affect every kernel-mode user, not just stale-cookie sessions.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@mho22 mho22 force-pushed the playground-cli-experimental-posix-kernel branch from aa0de73 to ff83a8f Compare May 12, 2026 14:41
@mho22 mho22 changed the title Experimental: WordPress Playground CLI under wasm-posix-kernel [wasm-posix-kernel] WordPress Playground CLI under wasm-posix-kernel - Proof of concept May 13, 2026
@mho22
Copy link
Copy Markdown
Collaborator Author

mho22 commented May 14, 2026

Closing in favor of #3634

@mho22 mho22 closed this May 14, 2026
mho22 added a commit that referenced this pull request May 17, 2026
… apply CLI constants pre-install

Address three Copilot review findings on PR #3604:

1. nginx was listening on 0.0.0.0 by default (no interface in the
   `listen` directive), which exposes the experimental dev server on
   the LAN even though the CLI advertises 127.0.0.1. The configs now
   use a `listen __HOST__:__PORT__` placeholder substituted by
   bootPosixKernelWordPress; the new `host` boot option defaults to
   `'127.0.0.1'` (kept symmetric with the existing `__PORT__`,
   `__SERVER_NAME__`, etc. placeholders so a future `--host` CLI
   flag becomes a one-line plumb-through).

2. wp-templates/wp-config.php defaulted `WP_DEBUG_DISPLAY` to true,
   while the classic CLI defaults it to false (run-cli.ts:723-724).
   The mismatch let PHP notices/warnings prepend HTML to JSON
   responses on early requests. Flipped to false to match.

3. mergeDefinedConstants(this.args) ran inside runBlueprint(), i.e.
   *after* bootWordPress()'s ensureWordPressInstalled() drove the
   WP installer. So `--define WP_DEBUG=...` and friends never made
   it into the install POST. Classic mode applies these via
   bootWordPress() before install; mirror that ordering by
   hoisting the loop into bootWordPress() before the install call.

Drive-bys from the host-placeholder change:
  - Renamed the kernel-runtime variable `host` to `kernelHost`
    inside bootPosixKernelWordPress to free up `host` for the
    network-bind concept (KernelRuntime.host → .kernelHost
    accordingly; not consumed by external callers).
  - waitForLoopback now takes the bind host explicitly. The FPM
    loopback probe still hits 127.0.0.1 (kernel-internal port),
    but the nginx probe matches the user-facing bind address.
  - Dropped the redundant URL from the "Booting WordPress…"
    pre-boot status print; run-cli.ts already emits a final
    "WordPress is ready at <serverUrl>" once boot completes.

All 16 kernel-mode specs pass (153 tests across 13 files in the
playground-cli vitest suite).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
mho22 added a commit that referenced this pull request May 17, 2026
…in CI

Two fixes for CI failures on PR #3604:

1. `node:url` was missing from getExternalModules() in
   vite-external-modules.ts. host-bridge.ts imports `pathToFileURL`
   from `node:url` for the dynamic-import URL of the kernel's host
   bundle, so production rollup builds (`nx build playground-cli`,
   `package-for-self-hosting`) failed with `"pathToFileURL" is not
   exported by "__vite-browser-external"`. Adding it alongside the
   existing `node:fs`, `node:crypto`, `node:http`, `node:net`,
   `node:process` entries lets rollup keep the import as external,
   matching how the file is actually consumed (Node-only).

2. The kernel's host bundle (wasm-posix-kernel/host/dist/index.js)
   leaves `fflate` and `fzstd` as external imports. They are
   declared in wasm-posix-kernel/host/package.json's dependencies,
   but the submodule's host/ directory is not part of the playground
   workspaces, so npm ci on the runners doesn't install them. The
   kernel-mode specs failed at `import { inflateSync } from "fflate"`.
   Add a CI step that runs `npm install --omit=dev --no-package-lock
   --ignore-scripts --prefix wasm-posix-kernel/host` before the
   test-playground-cli matrix's test step, populating
   wasm-posix-kernel/host/node_modules/{fflate,fzstd} where Node's
   resolver finds them when the host bundle is dynamic-imported.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants