Batch PHP support and bundle ABI updates with their runtime changes#907
Merged
Conversation
Delivering SIGPIPE for this existing EPIPE path changes observable signal and default-termination semantics. Advance ABI 18 to 19 and regenerate the ABI evidence in the same commit. Source-PR: #749 Folded coverage and documentation: a9652e91c.
Returning ENOENT for missing, PID-zero, and reaped procfs paths changes existing stat/access/path errno semantics. Advance ABI 20 to 21 and regenerate ABI evidence atomically. Source-PR: #744 Folded PHP procfs and utility coverage: 604dd7cba.
Socket-address marshalling, option pointers, accepted-fd state, routing, multicast behavior, hostname errors, fork wire data, and inherited socket ownership all change observable kernel/host behavior. Advance ABI 21 to 22 and regenerate ABI evidence atomically. Source-PR: #754 Curated-Reconstruction: retains the reviewed #754 epoch as one commit.
TCP FIN, EOF, EPIPE, wakeup, bridge-reader ownership, and queued-data behavior change existing socket observations. Add the real-reader export required by the host, advance ABI 22 to 23, and regenerate ABI evidence atomically. Source-PR: #755 Curated-Reconstruction: includes the reviewed queued-data detach repair in ABI epoch 23.
Wait and signal results, mmap placement and ownership, exec cleanup, descriptor/socket/shared-memory inheritance, fork metadata, epoll, sleep, and signal-death behavior all change existing process contracts. Advance ABI 25 to 26 and regenerate ABI evidence atomically. Require kernel_get_process_exit_signal so a missing kernel contract fails loudly. Retain file-backed MAP_SHARED handles across unlink, and drain last-owned host descriptors during forced process removal so the epoch is self-contained beneath its integration tests. Source-PR: #756 Folded-Repair: 01907159db3a092d76cc695b948f58bf0b52d0d7 Folded-Reconstruction: required-export model coverage b1eb8f24d and the shared-memory XFAIL retirement c8cd117a6.
Darwin login shells could select ambient CMake and make ahead of the declared dev-shell tools, while the fork-tool builder assumed the caller was in the repository root. Keep declared tools first and resolve builder inputs independently of the caller cwd so package and instrumentation results do not depend on undeclared host state. Folded-Reconstruction: 30ab29ff4 and 1fb6b9d60.
Rebuild PHP 8.3.15 and its dependencies through deterministic package recipes, add the canonical PHPT Node/browser harness and VFS assets, and keep verdict reporting and fork-instrumented output reproducible. Route the required __wasm_posix_vm_interrupt_after import from process workers to kernel workers on both browser and Node hosts. Validate fixed-width POSIX timer notification fields and suppress SIGEV_NONE wakeups. Advance ABI 26 to 27, bind the PHP package floor to 27, and regenerate the snapshot, host bindings, and libc header atomically. Source-PR: #740 Source-PR: #757
Correct no-follow pathname handling, omitted utimens values, mount policy, synthetic DNS and network peeking, exec/spawn preflight, exclusive creates, directory offsets, rename/unlink state, clocks, and process-worker lifecycle behavior. Keep Node and browser worker diagnostics and resource policy consistent, make SharedFS persistence and lazy backing race-safe, and retain the reviewed PHP shutdown, rootfs-service, netcat, and coarse-clock repairs with their tests and documentation. These changes alter existing path, errno, blocking, timer, process, and persistent-VFS semantics. Advance ABI 27 to 28 and regenerate the snapshot, host bindings, and libc header atomically. Source-PR: #757
The linked cancellation-point function type, 64-bit syscall argument slots, lseek state and errno behavior, and select/pselect blocking and caught-signal handling change existing ABI contracts. Advance ABI 28 to 29 and regenerate ABI evidence atomically. Source-PR: #758 Folded-Reconstruction: architecture-specific cancellation-slot build support 825712d55.
Fork and exec environment synchronization plus unchanged-ID chown handling alter existing launch, authorization, errno, and backend behavior. Advance ABI 29 to 30 and regenerate ABI evidence atomically. Source-PR: #742
Exercise the existing shared-listener and in-place exec behavior with one fork-instrumented guest on Node and Chromium. Cover a connection queued before exec, a second connection after exec, the CLOEXEC/non-CLOEXEC alias split, FIFO acceptance, and exact queue consumption. This replaces PR 745’s obsolete socket serialization approach with regression coverage only; it does not change runtime behavior or the ABI.
Move the maintained OpenSSL config and trust bundle into image-owned /etc/ssl state, and remove kernel and Node fabrication for caller-supplied images. Recursively merge canonical /etc into legacy browser filesystems without overwriting demo leaves, fail boot on incomplete overlays, bump the rootfs revision, and cover Node, Chromium, and PHP paths.
Write preflight, fork and exec state, exact-thread SIGXFSZ ownership, return values, and error behavior alter existing file-operation contracts. Advance ABI 30 to 31 and regenerate ABI evidence atomically. Source-PR: #748
Component walking, symlink and mount traversal, error selection, and canonical initial/current-directory behavior change existing pathname semantics. Advance ABI 31 to 32 and regenerate ABI evidence atomically. Source-PR: #741
Merge the address-sorted guest mapping and host reservation streams without materializing a combined list. Normalize fork-restored mappings once at their ingestion boundary so the ordering invariant is explicit. Reference-equivalence tests cover interleaving, equal starts, overlap, exact fits, exhaustion, and systematic relative orderings. The copied munmap regression from the original PR remains owned by #756. This is an unmeasured internal refactor. It makes no throughput or PHP/PHPT coverage claim and changes no intended host or ABI contract.
The existing descriptor map gains required lchown hosting, no-follow behavior, and different flag and errno semantics. Advance ABI 32 to 33 and regenerate ABI evidence atomically. Source-PR: #743
Pathconf return conventions, 64-bit output storage, syscall marshalling, the required host surface, errno behavior, and live-object interpretation change existing ABI semantics. Advance ABI 33 to 34 and regenerate ABI evidence atomically. Source-PR: #752
Wait/status/rusage marshalling, WNOWAIT, stop and continue state, output transactionality, blocking, and pthread-exit ownership change existing process ABI behavior. Advance ABI 34 to 35 and regenerate ABI evidence atomically. Source-PR: #759
PHP's ZIP extension needs one reproducible libzip package and one resolver-owned PHP build, but the source PR used hand-maintained source lists and retained checkout-local state that could drift from the pinned release. Build libzip 1.11.4 from its upstream CMake graph with explicit wasm32/musl facts and resolver-provided zlib. Publish only the archive, public headers, and relocatable metadata; verify required APIs, link every archive member, audit Wasm imports, and reject producer paths. In the same logical commit, build and declare PHP's zip.so against that exact package and route its runtime coverage through the normal Node and browser VFS paths. Source-era evidence includes byte-identical clean libzip output sets, complete archive/import accounting, focused host tests, selected ZIP PHPTs, and a real-Chromium create/close/reopen path. Final ABI-38 artifacts and broad gates remain the responsibility of the super-batch tip. Remaining limits: libzip is wasm32-only and enables STORE/DEFLATE without optional crypto, BZIP2, LZMA, or Zstandard backends. Source-PR: #647 Co-authored-by: mho22 <yannick@capsules.codes>
The original curl recipe reused a persistent checkout, emitted absolute cache paths, and let declared resolver dependencies diverge from what the program consumed. That made source builds vulnerable to stale state, collisions, and non-relocatable metadata. Build curl 8.11.1 in a fresh resolver transaction with explicit zlib and OpenSSL inputs, PIC/reproducibility flags, fatal configure-option checking, exact output declarations, all-member link/import audits, and producer-path checks. In the same logical commit, build PHP's curl.so against those artifacts and add forked HTTP coverage through the normal kernel and host networking paths. Source-era evidence includes clean libcurl/curl resolves, complete archive/import accounting, exact output checks, CLI tests, and Node/Chromium PHP HTTP coverage. Final ABI-38 artifacts and broad gates remain the responsibility of the super-batch tip. HTTPS certificate flows and arbitrary remote endpoints are not claimed by this focused coverage. Source-PR: #648 Co-authored-by: Yannick Decat <yannick@capsules.codes>
PHP intl needs reproducible ICU and libc++ inputs plus fail-closed C++ side-module linking before the host/guest replay contract can advance safely. Build PIC ICU/libc++ outputs without producer paths, declare non-Wasm runtime files explicitly, complete the initial intl.so link path, reject unresolved C++ imports, and cover exceptions and fork replay. This compatible setup intentionally precedes ABI 36; the next commit owns the incompatible replay-memory and pthread-fork contract, while the following PHP commit owns final ICU runtime-data materialization. Source-era package and focused runtime evidence is retained as review provenance, but the final ABI-38 artifact rebuild and complete Node/browser gate apply at the super-batch tip. Source-PR: #839 Source-PR: #894 Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
intl.so needs the exact resolver-selected ICU data blob at runtime. Cache scanning and host-only path overrides made local, fetched, Node, VFS, and browser executions observe different closures and could leave a present extension without matching data. Copy the selected ICU data into the PHP program archive as declared /usr/lib/php/icu.dat state, keep general runtime files in one artifact tier, query structured package metadata from image/test builders, and reject incomplete VFS staging. The PHP consumers now follow the ABI-36 side-module replay contract established by the preceding epoch. Source-era evidence covers reproducible ICU/libc++ outputs, runtime metadata and byte identity, complete PHP artifacts, focused Node/browser intl behavior, and fail-closed stale-artifact handling. Those results are provenance rather than a claim about an ABI-19 artifact; final ABI-38 archives and broad gates remain the super-batch tip's responsibility. Pthread-side dynamic loading remains truthfully unsupported. Source-PR: #839 Source-PR: #894
Capture guest stdout and stderr through one ordered runner sink while retaining host-runtime messages in bounded, per-test diagnostic sidecars. This prevents nonzero-exit warnings from contaminating guest expectation comparisons without hiding them from CI logs. Record the five currently unsupported or indeterminate basic cases as explicit XFAILs, and add narrow Kandelo candidates for empty successful SO_BINDTODEVICE probes and signal-before-EPIPE output ordering. Aggregate repair. Preserves guest-only expectations while reporting host runtime failures independently.
Dinit client tools use C++ exceptions, but compiling without the WebAssembly exception model emitted throws without catch handlers and turned an ordinary dinitctl connection failure into a host WebAssembly.Exception. Compile target C++ with -fwasm-exceptions and verify a missing control socket exits truthfully with the expected diagnostic. Aggregate package repair discovered by the PHP extension gate.
MariaDB mysqltest on wasm64 connected successfully, then musl passed SO_RCVTIMEO/SO_SNDTIMEO as option numbers 20/21. The kernel recognized only wasm32 time64 numbers 66/67 and returned ENOPROTOOPT. Canonicalize both musl number pairs to the existing receive/send timeout state. This adds valid option-number compatibility without changing existing meanings; the ABI snapshot check reports no structural drift. Validated with the wasm32/wasm64 socket-timeout guest Vitest cases, the targeted native socket-timeout unit test, scripts/check-abi-version.sh, and the exact-c98 MariaDB Aria benchmark on both guest widths. Aggregate socket compatibility repair retained after the ABI epochs it exercises.
brandonpayton
added a commit
that referenced
this pull request
Jul 13, 2026
The deterministic region ordering already landed through #907, but its in-process regression does not vary Rust HashMap random state. Run the CLI in twelve fresh processes against an alternating-type nested-region fixture and compare the emitted bytes. Document the cross-process byte-reproducibility contract and update the fork-instrumentation reference to the current ABI 39. This adds no ABI or package-artifact change. Validation: the full fork-instrument suite passed 187 tests; the focused fresh-process test passed; and the ABI snapshot check passed through scripts/dev-shell.sh.
This was referenced Jul 13, 2026
brandonpayton
added a commit
that referenced
this pull request
Jul 13, 2026
The deterministic region ordering already landed through #907, but its in-process regression does not vary Rust HashMap random state. Run the CLI in twelve fresh processes against an alternating-type nested-region fixture and compare the emitted bytes. Document the cross-process byte-reproducibility contract and update the fork-instrumentation reference to the current ABI 39. This adds no ABI or package-artifact change. Validation: the full fork-instrument suite passed 187 tests; the focused fresh-process test passed; and the ABI snapshot check passed through scripts/dev-shell.sh.
brandonpayton
added a commit
that referenced
this pull request
Jul 13, 2026
Move the recursive /etc merge landed with #907 out of the browser app and into the shared host VFS layer. Preserve caller-owned entries and source metadata, and keep missing canonical state, short reads, and capacity failures visible during image assembly.
brandonpayton
added a commit
that referenced
this pull request
Jul 13, 2026
The deterministic region ordering already landed through #907, but its in-process regression does not vary Rust HashMap random state. Run the CLI in twelve fresh processes against an alternating-type nested-region fixture and compare the emitted bytes. Document the cross-process byte-reproducibility contract and update the fork-instrumentation reference to the current ABI 39. This adds no ABI or package-artifact change. Validation: the full fork-instrument suite passed 187 tests; the focused fresh-process test passed; and the ABI snapshot check passed through scripts/dev-shell.sh.
brandonpayton
added a commit
that referenced
this pull request
Jul 13, 2026
Move the recursive /etc merge landed with #907 out of the browser app and into the shared host VFS layer. Preserve caller-owned entries and source metadata, and keep missing canonical state, short reads, and capacity failures visible during image assembly.
brandonpayton
added a commit
that referenced
this pull request
Jul 14, 2026
The deterministic region ordering already landed through #907, but its in-process regression does not vary Rust HashMap random state. Run the CLI in twelve fresh processes against an alternating-type nested-region fixture and compare the emitted bytes. Document the cross-process byte-reproducibility contract and update the fork-instrumentation reference to the current ABI 39. This adds no ABI or package-artifact change. Validation: the full fork-instrument suite passed 187 tests; the focused fresh-process test passed; and the ABI snapshot check passed through scripts/dev-shell.sh.
brandonpayton
added a commit
that referenced
this pull request
Jul 14, 2026
Move the recursive /etc merge landed with #907 out of the browser app and into the shared host VFS layer. Preserve caller-owned entries and source metadata, and keep missing canonical state, short reads, and capacity failures visible during image assembly.
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.
What this PR does
This PR brings together all of the PHP work we plan to merge. It combines the
changes from #866, #871, and #894 with five related PRs that were reviewed
separately: #845, #856, #884, #905, and #906.
The main result is PHP 8.3.15 with the
zip,curl, andintlextensions,built and run through the same Kandelo systems used by other software: the SDK,
C library, package resolver, virtual filesystem, kernel, and shared
Node.js/browser runtime.
Porting PHP turned out to be a pretty good stress test for Kandelo. It exposed
bugs and missing Unix behavior in processes, files, networking, signals,
timers, and the browser runtime. This batch fixes those problems in Kandelo
itself instead of adding PHP-only workarounds.
What this adds
filesystem image, tested with PHP's PHPT suite, and launched from the browser
demo.
zip.so,curl.so, andintl.soas regular optional packages—not localcheckout additions or demo shortcuts.
fork,exec,wait, pthread cleanup, signals,and POSIX timers.
limits, path lookup, symlinks,
pathconf, and filesystem metadata.broadcast, AF_UNIX sockets, nonblocking connections, and socket errors.
cancellation,
select/pselect, and worker coordination.images, browser assets, test diagnostics, and ABI documentation.
The affected runtime code is shared by Node.js and browser hosts. Both hosts
passed the final staging suites; the hands-on browser feature checks are still
listed below.
Why the ABI updates stay bundled with their runtime changes
The PHP work is batched into one PR because its package, the host runtime, and
the kernel all have to agree on the same ABI. The ABI is simply the binary
contract those pieces use to talk to one another.
The first 42 commits are the reviewed PHP and ABI sequence. They stay in a
straight line, with no merge commits, and we plan to rebase-merge them in that
order. Whenever a runtime change breaks the binary contract, that same commit:
ABI_VERSION; andThat bundling keeps every version from 19 through 39 internally consistent. A
later ABI bump cannot repair an earlier commit whose code and generated files
disagreed.
Changes that do not break the ABI get their own commits when that makes them
easier to review or revert. Staging found three more real issues after the
42-commit sequence was locked, so those fixes remain as three focused follow-up
commits instead of rewriting already-reviewed history.
We do not rebuild PHP, packages, or filesystem images after every commit in the
batch. We rebuild them together from the final head. The one historical
exception is #740/#757: PHP needs the VM-interrupt hook from #757, so those
pieces share one commit and never leave an unusable intermediate revision.
Exact version under review
candidate/php-abi-superbatch-curated-v20f464442db59397fab7098ae154d7e9640cc345c8bd862d26ef90d31cbfb61d0de504bdcfd7053b2mainwhen this branch was published:2c083e143618e4974a637b649e6221c64f0dd936The planned 42-commit PHP/ABI sequence has not been added to, removed from, or
reordered. The final three commits fix concrete CI serialization, browser
filesystem-image, and dinit package failures found during staging. None changes
the ABI, so the final ABI version remains 39.
Fresh staging and every automated test suite are now green on this head. The
remaining work is the full PHP inventory, hands-on browser feature checks, the
performance decision, and final approval.
Full ABI 19–39 commit map
Each row is one commit that updates the implementation,
ABI_VERSION,abi/snapshot.json,libc/glue/abi_constants.h, andhost/src/generated/abi.tstogether.4dedef7f4EPIPEerrors to the correctSIGPIPEbehavior551fe6129mremapflags911acf77e9c15c97287283311def516785eb2208a2a6fMSG_TRUNCand zero-length pointers9e07b4d73fork,exec, file descriptor, and memory ownership99c0561fc22e72dfd3477b9c73bselect/pselectcalls702f72834chownsentinel values71bb2a2f7579fd76be253ff6cf2b1920638epathconfmarshalling for live objects5dc0184817a112faf3fork4ecf44abeabc2b8ef7bcb02e938Each of these 21 commits passed the ABI check against the commit immediately
before it. The kernel also compiled at every ABI-changing commit from 19
through 38. ABI 39 passed the exact-commit kernel tests listed below.
The current example in
docs/porting-guide.mdnow uses ABI 39.What is included
The three main PHP PRs are #866, #871, and #894. Their changes are included
here in dependency order instead of merging those PR branches into one another.
The ABI-changing work is listed above. These useful changes do not need an ABI
bump, so they remain easy to review on their own:
exec;mmapstrategy, with no performanceclaim;
zip.so;curl.so.Running everything together also uncovered smaller bugs. This branch includes
fixes for PHPT result reporting, OpenSSL/PHP feature reporting, process cleanup,
Sortix diagnostics, dinit exceptions, shared mappings after unlink, queued TCP
data after detach, side-module buffer limits, browser shutdown and scheduling,
socket timeout layouts, memory64 fixtures, macOS program lookup, benchmark
evidence, and deferred cancellation. The commit messages keep the source and
reasoning for each fix.
Follow-up fixes found by staging
The locked batch exposed three additional problems when it ran in the final CI
and browser paths:
too many expensive host tests at once. The new setting restores serialized CI
execution and the affected tests now wait for the real runtime state.
/etc. It nowmerges the full directory tree, including nested certificate files, while
preserving files supplied by the caller.
package now lets that signal reach dinit's existing handler without disabling
normal C++ exception reporting.
Each fix is a separate final commit because each has a different cause and can
be reviewed or reverted on its own. None changes the ABI.
Other PRs included in this batch
e9e3673fd. It catches fork-buffer overruns and applies the same guard to main, pthread, and side-module buffers.SO_ERROR.getppid()was already correct.siginfo_tlayout conversion.Those source PRs remain open as review records. Including their work here does
not merge, retarget, or separately approve them.
Reviewed work not included in this batch
buffers.
inodes.
Any useful remaining work needs a new review.
version. It is not part of this PR.
or rebuilt before they could be considered.
Checks run on the current head
“Current head” here means
0f464442d.git diff --check origin/main..HEADThe complete staging run is
29224188992.
It rebuilt or resolved the exact-key artifacts, prepared one shared test
workspace, and passed every split test suite against it. The local app also
launched through
./run.sh --pr-staging --fetch-only browserand returnedHTTP 200 with the required browser isolation headers.
The ABI-39 commit itself is still
bcb02e938. At that commit, the ABIchecker verified the snapshot, C output, TypeScript output, and required
timer-fire export, and the kernel/shared checks passed. The three follow-up
commits do not touch the ABI files or Rust kernel.
The only pending GitHub status is
merge-gate. That is the expected manualready-to-ship gate, not a failed test. It should stay pending until the remaining
manual checks and approval are complete.
This evidence covers the final package matrix and automated conformance suites.
It does not yet claim the full PHP PHPT inventory, the hands-on browser feature
pass, or a performance result.
Useful earlier results from ABI 38
The previous ABI-38 checkpoint had:
ABI-38 musl, programs, packages, host bundles, and root filesystem were rebuilt.
Dedicated scheduler-affinity coverage also passed in real Chromium. These runs
had no unexpected failures, build errors, or timeouts.
That is useful context, but it is not final ABI-39 evidence.
Fresh wasm32 and wasm64 musl builds, plus the normal program build, also
completed while #906 was being rebuilt. They happened before the final
shared-host
siginfo_tfix, so they do not replace a clean build from the exactcommit above.
A local
build.shrun was deliberately stopped during the expensive ABI-39package and filesystem-image build. It is not counted as a passing build.
What is still needed before merge
commit keeps its code change, version bump, and generated files together.
checks.
fork instrumenter, programs, host bundles, packages, PHP extensions,
filesystem images, and browser assets.
artifact set.
unchanged source and artifact set. Keep both structured results and raw
output.
verify that the app is served with browser isolation enabled.
and user-visible error reporting in that browser app.
the risk was not measured. This PR currently makes no performance claim.
files changed unexpectedly.
the durable release workflow can turn
merge-gategreen.Known limits
an explicit unsupported error instead of pretending it worked.
zip.sopath were notincluded.
remote endpoints.
behavior, or any performance result. Those claims depend on the final checks
above.
How to merge this
them into one another or into
mainfirst.gh pr merge --rebase.one-commit-per-ABI-change structure that was reviewed.
Brandon has explicitly approved it.