[wasm-posix-kernel] WordPress Playground CLI under wasm-posix-kernel - Proof of concept#3634
Draft
mho22 wants to merge 20 commits into
Draft
[wasm-posix-kernel] WordPress Playground CLI under wasm-posix-kernel - Proof of concept#3634mho22 wants to merge 20 commits into
mho22 wants to merge 20 commits into
Conversation
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>
Contributor
There was a problem hiding this comment.
Pull request overview
Note
Copilot was unable to run its full agentic suite in this review.
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 PHP.wasm. Marked as a proof-of-concept depending on five unmerged kernel-side fixes.
Changes:
- New CLI flag and
PosixKernelHandlerthat boots nginx + PHP-FPM in the kernel and serves WordPress through a single FastCGI router script KernelLimitedPHPApishim implementing the v1 blueprint API surface against the kernel-resident WordPress (host fs + HTTP + spawnedphp.wasm)- WordPress auto-prepare pipeline (download, SQLite, mu-plugins) and a kernel-mode test suite gated by
WASM_POSIX_KERNEL_DIR
Reviewed changes
Copilot reviewed 27 out of 27 changed files in this pull request and generated 12 comments.
Show a summary per file
| File | Description |
|---|---|
| wasm-posix-kernel | New git submodule pointing at the artifacts branch |
| .gitmodules | Adds the wasm-posix-kernel submodule entry |
| .github/workflows/ci.yml | Bumps Node to 24 and installs kernel host runtime deps |
| packages/vite-extensions/vite-external-modules.ts | Adds node:url to externalized modules |
| packages/playground/cli/vite.config.ts | Wires the kernel-mode test setup file |
| packages/playground/cli/tsconfig.{lib,spec}.json | Adds vite/client types for ?raw imports |
| packages/playground/cli/src/run-cli.ts | Adds --experimental-posix-kernel flag and dispatch path |
| packages/playground/cli/src/start-server.ts | New reserveFreePort() helper |
| packages/playground/cli/src/posix-kernel/host-bridge.ts | Resolves the kernel checkout from WASM_POSIX_KERNEL_DIR |
| packages/playground/cli/src/posix-kernel/boot.ts | Boots nginx + php-fpm inside the kernel |
| packages/playground/cli/src/posix-kernel/php-api.ts | LimitedPHPApi-shaped shim over host fs / HTTP / kernel spawn |
| packages/playground/cli/src/posix-kernel/posix-kernel-handler.ts | Orchestrates boot, install, and blueprint v1 execution |
| packages/playground/cli/src/posix-kernel/prepare-wordpress.ts | Downloads + lays out WordPress and SQLite integration |
| packages/playground/cli/src/posix-kernel/router.php | Single FastCGI entry point handling static / PHP / WP routing |
| packages/playground/cli/src/posix-kernel/wp-templates/* | wp-config, auto-login, defines, disable-wp-mail templates |
| packages/playground/cli/src/posix-kernel/configs/{nginx,php-fpm}.conf | nginx + php-fpm configs templated at boot |
| packages/playground/cli/tests/posix-kernel/* | Kernel-mode test suite (auto-prepare, boot, blueprint-v1, php-api, run-cli) |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
c0418c4 to
905f8ba
Compare
… I/O Round 3 (905f8ba) handed PHP-FPM the user's --mount via the host path in SCRIPT_FILENAME / DOCUMENT_ROOT. On Windows that value is `C:\…`; PHP-FPM's musl-libc checks `path[0] == '/'` to decide "absolute", treats `C:` as relative, joins it onto cwd, and never gets to Node `fs.openSync` — surfacing as 14 test failures on windows-latest CI for PR #3634. Round 2's NTFS junction was strictly closer to working but added its own breakage (1 failure: 200 with empty body, ENOENT through the junction inside the kernel). This change moves the Windows path translation to where it belongs — the kernel's Node-side host bridge — and has the CLI hand the kernel only POSIX-shaped paths. The submodule bump pairs with e178b0f3 in wasm-posix-kernel: NodePlatformIO.rewritePath now translates `/C/foo` -> `C:/foo` on win32 before each fs.* call. CLI changes: * `temp-dir.ts` (new): the booter's temp dir is a regular `tmp-promise` directory. The /dev/shm/ overload from 2bd8b02 is gone — with the host bridge translating any `/<letter>/...`, a `toPosixPath(nativeDir.path)` view of the same dir flows through the same mechanism. * `boot.ts`: takes paired host/kernel paths (`wordPressRoot{Host,Kernel}Path`, `tempDir{Host,Kernel}Path`). Substitutes `__TEMP_DIR__`, `__WORDPRESS_ROOT__`, `__FPM_PORT__` in nginx.conf with kernel-shaped values. Drops the local `toPosixSeparators` helper. * `php-api.ts`: `documentRoot = toPosixPath(hostRoot)` so blueprint v1's phpVar embedding of documentRoot produces a path PHP inside the kernel can resolve. cwd / DOCROOT / translateVfsPathsInCode / scriptPath argv all use the POSIX form. `hostRoot` stays native for this class's own Node fs.*. * `posix-kernel-handler.ts`: computes `wordPressRootKernelPath = toPosixPath(wordPressRootHostPath)` and passes both to the booter. Uses createPosixKernelTempDir(). * `configs/nginx.conf`: re-applies `__TEMP_DIR__` for nginx's pid and *_temp_path directives (the bundled nginx wasm was built with `/tmp/nginx_*_temp` defaults, which on Windows has no /tmp/ on the host fs). `fastcgi_pass` uses the per-boot `__FPM_PORT__`. * `configs/php-fpm.conf`: `listen = 127.0.0.1:__FPM_PORT__` so concurrent vitest workers don't EADDRINUSE on 9000. History note: this branch was force-reset back to ff83a8f (drops the broken-on-Windows round 3 plus the now-superseded /dev/shm tempdir trick) before this commit. The earlier round-3 baseline is not preserved in linear history. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
905f8ba to
11a61a2
Compare
…itedPHPApi.toHost Round 5 of the Windows-CI fix sequence for `--experimental-posix-kernel`. Addresses the two failures left over from round 4 on test-playground-cli (windows-latest): 1. `should run blueprint including git:resources` — `installPlugin` builds `pluginDirectoryPath = joinPaths(await playground.documentRoot, 'wp-content', 'plugins', 'blocky-formats')`. On Windows `documentRoot` is the POSIX form (`/C/Users/.../wordpress`), so the plugin path becomes `/C/Users/.../wordpress/wp-content/plugins/ blocky-formats`. `KernelLimitedPHPApi.toHost()` previously only rewrote the `/wordpress` VFS prefix, leaving `/C/...` paths to flow into Node `fs.*` unchanged — which on Windows resolves relative to the current drive instead of the real host root. The plugin files ended up in the wrong directory, kernel-side `glob()` found nothing, and activation failed. Extend `toHost` to also recognize `documentRoot` / `documentRoot/...` prefixes and translate them back to native `hostRoot`. No-op on macOS/Linux where the two are equal. 2. `should use default site-url when not provided` — the test asked for `port: 9500` and asserted the response body contained `http://127.0.0.1:9500`. Under vitest's file parallelism on Windows it raced the same-named test in `tests/run-cli.spec.ts`, which also wants 9500. The posix-kernel handler silently falls back to a free port when 9500 is busy (intentional design, covered by `port in use > uses a free port when the requested port is already taken`), so the assertion saw a random port instead. Switch the test to `port: 0` and assert against `cliServer.serverUrl`'s actual origin — preserves the intent (siteurl matches the bound URL) and doesn't contest 9500. macOS suite: 154/154 still pass. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Rebases the wasm-posix-kernel submodule branch playground-cli-experimental-posix-kernel-with-artifacts onto current origin/main (42 commits forward). The superseded "set 128 KiB stack-size" commit was dropped — main already enforces a 4 MB stack via -Wl,-z,stack-size=4194304.
2252902 to
0c51a67
Compare
…("nobody")
The submodule's PR #442 (vfs cutover) deleted the kernel's synthetic
/etc/passwd|group|hosts files. When NodeKernelHost is constructed
without rootfsImage, the new NodePlatformIO reads the host's real
/etc/passwd — on macOS that returns uid=-2 for "nobody", which
musl's getpwnam parses as invalid, so php-fpm and nginx both abort
their pool startup with "cannot get uid for user 'nobody'" /
"getpwnam('nobody') failed".
This commit cuts the playground CLI over to the rootfs-based VFS
model:
- Bumps the wasm-posix-kernel submodule to a tip that adds an
`extraMounts` option on NodeKernelHostOptions (and bundles the
rebuilt rootfs.vfs image alongside the kernel binaries).
- boot.ts now passes `rootfsImage: "default"` so the worker
mounts host/wasm/rootfs.vfs at "/" with the canonical scratch
layout (/tmp, /var/*, /home/user, /root, /srv). rootfs/etc/passwd
in the image carries the standard `nobody:x:65534:65534:...`
line that musl accepts.
- boot.ts also passes extraMounts so the per-boot temp dir
(nginx.conf, php-fpm.conf, router.php, fastcgi/client_body temp
dirs, logs) and the wordPress document root remain reachable
through the kernel VFS — without them, the longest-prefix mount
resolver hits /var/folders/... (macOS) or /tmp/... (Linux) and
falls through to either the scratch /tmp memfs or the rootfs
image, both of which 404 the files nginx and FPM need.
- host-bridge.ts widens the typed surface to mirror the upstream
additions (rootfsImage + extraMounts).
Verified locally on Node 24 (Darwin) — all 11 posix-kernel
run-cli.spec.ts tests pass and getpwnam/getgrnam succeed inside
the kernel without the synthetic /etc fallback.
0c51a67 to
16470c7
Compare
The submodule's HostFileSystem.safePath rejected every mount-relative path on Windows with EACCES because the traversal guard compared against `this.rootPath + "/"` instead of the platform-native separator. The kernel surfaced the throw as errno 13, which caused both nginx and php-fpm to abort startup with "Permission denied" when opening their config files from extra mounts (per the Windows CI failure on 76524882511). Bumps the submodule pointer to ac47b26e (source) / d4e8610991fcfa4e8e96746d52a6fd71fb60a63f (artifacts) so CI picks up the one-character fix (`"/"` -> `nodePath.sep`).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds an experimental
--experimental-posix-kernelflag 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.
What this adds
A new
--experimental-posix-kernelCLI flag that:WASM_POSIX_KERNEL_DIRcheckout.index.php(no PCRE in the nginx build).--mountis given) and runs the install over HTTP POST against the in-kernel server.KernelLimitedPHPApishim so existing v1 blueprint steps (runPHP,installPlugin,writeFile,setSiteOptions,login, etc.) work against the kernel-resident WordPress.playground_auto_login_already_happenedcookies on the first request via a filesystem marker (avoids a 302 loop after--loginruns).Demo:
Commits
b1f102aExperimental posix-kernel: nginx + PHP-FPM boot pipeline9b7c2e6Experimental posix-kernel: KernelLimitedPHPApi shim7e8665cExperimental posix-kernel: WordPress preparation1834dd4Experimental posix-kernel: handler + --experimental-posix-kernel CLI flag628bdfaExperimental posix-kernel: kernel-mode test suited8afabeposix-kernel: clear stale auto-login cookie via first-request markeraa56dbdposix-kernel: require WASM_POSIX_KERNEL_DIR env var38fb9cfposix-kernel: quiet nginx error_log + disable access_logWhy this is experimental
fcgi_read_request buf[65543]overflows BSS into musl'svmlock, causing the install POST to deadlock on__vm_wait).wakeBlockedPollsnapshot fix (live Map iteration livelocked the worker under nginx + PHP-FPM I/O patterns).node-kernel-worker-entrybundle fix (worker entry wasn't emitted by tsup, so consumers couldn'trequire.resolveit from the publisheddist/).configs/nginx.conf,configs/php-fpm.conf,router.php,wp-templates/). The dev path resolves them directly fromsrc/. See "Open follow-ups".Open follow-ups (deliberately out of scope)
posix-kernel/configs/,router.php, andwp-templates/intodist/.--site-urlsupport under kernel mode. The kernel handler currently ignoresargs['site-url'];wp-templates/wp-config.phpderivesWP_HOME/WP_SITEURLfrom the requestHostheader.intl.so+icudt74l.datinto the kernel filesystem and wireextension=intl.host-bridge.tscan drop theWASM_POSIX_KERNEL_DIRindirection and switch to a regular package import.Test plan
The 16 kernel-mode tests in
packages/playground/cli/tests/posix-kernel/*.spec.tspass locally against a built wasm-posix-kernel checkout (HEAD ofmho22:playground-cli-experimental-posix-kernel):They will fail in stock CI without a kernel checkout — expected at this stage. See "Why this is experimental".
🤖 Generated with Claude Code