Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 78 additions & 0 deletions docs/native-php-runtime.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
# Native PHP Runtime Adapter

`runtime.backend: "wordpress-native"` selects the native adapter without changing
backend-neutral recipe command IDs. The adapter never substitutes host PHP,
ambient credentials, or production state when a contained driver is unavailable.

## Built-in Driver

`wordpress-native` now uses the built-in Docker driver when no `backendPackage` is
declared. It creates an isolated internal Docker network, a disposable MariaDB
sidecar on tmpfs, and a digest-pinned WordPress/PHP Apache container. Apache prefork
is configured with two persistent workers and PHP OPcache has timestamp validation
configuration. Docker is a hard requirement: the adapter fails closed rather than
using host PHP or host credentials.

The driver creates a random fixture-only database/admin secret for every run. It
never imports a host browser profile. `wordpress.browser-actions` captures a
machine-readable local network record, and `wordpress.bench` emits cold startup,
warm no-op PHP, and dynamic HTTP timings marked as local evidence, not production
RUM. The native artifact bundle contains `files/native/browser-network.json` and
`files/native/commands.json` alongside `native-runtime-provenance.json`.

## Optional Driver Contract

The CLI can load an adapter-owned contained driver from `runtime.backendPackage` when
the backend is `wordpress-native`. Its package uses `kind: "native"` and exports
`createNativeRuntimeDriver()`. The driver must report a digest-pinned container
image, PHP version and SAPI, persistent enabled OPcache evidence, at least two HTTP
workers, and a disposable managed-runtime-service database. Startup rejects
incomplete evidence and destroys a partially created runtime.

Before accepting commands, the driver persists
`wp-codebox/native-runtime-provenance/v1` in the run artifact directory and returns
its path and SHA-256. The evidence identifies the backend, PHP/SAPI, image digest,
OPcache configuration/status, worker model, managed database integration, and that
measurements are local representative evidence rather than production RUM. The
driver owns process and container lifecycle; `destroy()` is single-flight and the
adapter terminalizes the runtime even when driver cleanup reports an error.

Browser commands remain backend-neutral. A native driver must expose the local
preview and authenticated fixture workflow through the existing browser-action
contract; it must not use caller browser credentials.

`wordpress.browser-actions` must declare `auth=wordpress-admin` or
`auth=storage-state`. Native drivers accept only runtime-generated fixture state;
they must not import a caller profile or ambient browser credentials.

## HTTP Concurrency

The driver records its resolved worker count and worker model in
`provenance.httpConcurrency`. This is local representative evidence, not
production RUM. Benchmark workloads should record cold startup, warm no-op PHP,
and a dynamic WordPress request for both `wordpress-playground` and
`wordpress-native` using `wordpress.bench`.

The native provenance contract records that all three cases are covered. Cold
startup crosses a worker/process boundary, warm no-op PHP retains the shared
OPcache, and the dynamic request exercises WordPress routing. These are local
representative measurements, not production RUM.

## Example optional selection

```json
{
"runtime": {
"backend": "wordpress-native",
"backendPackage": {
"kind": "native",
"source": "./contained-native-driver"
}
}
}
```

The optional native package is responsible for translating the existing backend-neutral
`wordpress.*` commands, including browser actions and fixture authentication, into
its contained runtime. It must not read host PHP configuration, browser profiles,
ambient credentials, or production state.
14 changes: 14 additions & 0 deletions npm-shrinkwrap.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 12 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,10 @@
"types": "./packages/runtime-playground/dist/public.d.ts",
"import": "./packages/runtime-playground/dist/public.js"
},
"./native": {
"types": "./packages/runtime-native/dist/index.d.ts",
"import": "./packages/runtime-native/dist/index.js"
},
"./cli": {
"types": "./packages/cli/dist/index.d.ts",
"import": "./packages/cli/dist/index.js"
Expand All @@ -75,6 +79,8 @@
"files": [
"packages/runtime-core/dist",
"packages/runtime-core/package.json",
"packages/runtime-native/dist",
"packages/runtime-native/package.json",
"packages/runtime-playground/dist",
"packages/runtime-playground/package.json",
"packages/cli/dist",
Expand All @@ -88,8 +94,8 @@
"LICENSE"
],
"scripts": {
"build": "node ./node_modules/typescript/bin/tsc -b packages/runtime-core packages/runtime-playground packages/cli && node scripts/ensure-cli-bin-executable.mjs && tsx scripts/write-cli-build-provenance.ts",
"build:release": "node ./node_modules/typescript/bin/tsc -b --force packages/runtime-core packages/runtime-playground packages/cli && node scripts/ensure-cli-bin-executable.mjs && tsx scripts/write-cli-build-provenance.ts",
"build": "node ./node_modules/typescript/bin/tsc -b --force packages/runtime-core packages/runtime-playground packages/runtime-native packages/cli && node scripts/ensure-cli-bin-executable.mjs && tsx scripts/write-cli-build-provenance.ts",
"build:release": "node ./node_modules/typescript/bin/tsc -b --force packages/runtime-core packages/runtime-playground packages/runtime-native packages/cli && node scripts/ensure-cli-bin-executable.mjs && tsx scripts/write-cli-build-provenance.ts",
"cloudflare:build": "npm --prefix packages/runtime-cloudflare run build",
"cloudflare:check": "npm --prefix packages/runtime-cloudflare run check",
"cloudflare:package-dry-run": "npm --prefix packages/runtime-cloudflare run package:dry-run",
Expand Down Expand Up @@ -156,6 +162,8 @@
"test:runtime-command-artifact-bounds": "tsx tests/runtime-command-artifact-bounds.test.ts",
"test:native-agent-task-interruption": "node tests/execute-native-agent-task-interruption.test.mjs",
"test:native-agent-task-playground-e2e": "tsx tests/execute-native-agent-task-playground-e2e.test.ts",
"test:native-runtime": "tsx tests/native-runtime.test.ts && tsx scripts/backend-package-adapter-registry-smoke.ts",
"test:native-docker-runtime": "npm run build && tsx tests/native-docker-runtime.integration.test.ts",
"test:bench-command-step-behavior": "tsx tests/bench-command-step-behavior.test.ts",
"test:external-http-load-integration": "npm run build && tsx tests/external-http-load.integration.test.ts",
"test:generic-primitives": "npm run test:artifact-path-primitives && npm run test:browser-callback-materialization-contracts && npm run test:source-package-compiler-primitives && npm run test:bench-command-step-behavior && npm run test:generic-ability-runtime-run",
Expand All @@ -181,6 +189,7 @@
"workspaces": [
"packages/cli",
"packages/runtime-core",
"packages/runtime-native",
"packages/runtime-playground",
"packages/wordpress-plugin"
],
Expand All @@ -198,6 +207,7 @@
"dependencies": {
"@automattic/wp-codebox-cli": "file:packages/cli",
"@automattic/wp-codebox-core": "file:packages/runtime-core",
"@automattic/wp-codebox-native": "file:packages/runtime-native",
"@automattic/wp-codebox-playground": "file:packages/runtime-playground",
"@php-wasm/node-8-3": "file:runtime-overlays/php-wasm-node-8-3",
"@php-wasm/node-8-4": "3.1.46",
Expand Down
1 change: 1 addition & 0 deletions packages/cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
},
"dependencies": {
"@automattic/wp-codebox-core": "file:../runtime-core",
"@automattic/wp-codebox-native": "file:../runtime-native",
"@automattic/wp-codebox-playground": "file:../runtime-playground"
}
}
25 changes: 24 additions & 1 deletion packages/cli/src/recipe-backend-package.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { readFile, stat } from "node:fs/promises"
import { basename, dirname, join, resolve } from "node:path"
import { pathToFileURL } from "node:url"
import { normalizeRuntimeBackendKind, type RuntimeBackendFactoryContext, type RuntimeBackendKind, type WorkspaceRecipe, type WorkspaceRecipeRuntimeBackendPackage } from "@automattic/wp-codebox-core"
import type { NativeRuntimeDriverFactory } from "@automattic/wp-codebox-native"

export interface RuntimeBackendPackageProvenance {
schema: "wp-codebox/runtime-backend-package/v1"
Expand Down Expand Up @@ -94,7 +95,25 @@ const playgroundRuntimeBackendPackageAdapter: RuntimeBackendPackageAdapter = {
},
}

const runtimeBackendPackageAdapterRegistry = new RuntimeBackendPackageAdapterRegistry([playgroundRuntimeBackendPackageAdapter])
const nativeRuntimeBackendPackageAdapter: RuntimeBackendPackageAdapter = {
backendKind: "wordpress-native",
prepare(loadedPackage) {
const { backendPackage, entrypoint, module } = loadedPackage
if (backendPackage.kind !== "native") {
throw backendPackageError(backendPackage, `Unsupported native WordPress runtime backend package kind: ${backendPackage.kind}`)
}
if (!isNativeRuntimeDriverFactory(module)) {
throw backendPackageError(backendPackage, `Runtime backend package entrypoint must export createNativeRuntimeDriver(): ${entrypoint}`)
}

return {
runtimeBackendContext: { nativeRuntimeDriver: (module as NativeRuntimeDriverFactory).createNativeRuntimeDriver() },
diagnostics: [{ status: "passed", message: "Entrypoint exports createNativeRuntimeDriver" }],
}
},
}

const runtimeBackendPackageAdapterRegistry = new RuntimeBackendPackageAdapterRegistry([playgroundRuntimeBackendPackageAdapter, nativeRuntimeBackendPackageAdapter])

export class RecipeRuntimeBackendPackageError extends Error {
readonly code = "recipe-runtime-backend-package-invalid"
Expand Down Expand Up @@ -223,6 +242,10 @@ function isPlaygroundCliModule(value: unknown): value is RuntimeCliEntrypointMod
return Boolean(value && typeof value === "object" && "runCLI" in value && typeof (value as { runCLI?: unknown }).runCLI === "function")
}

function isNativeRuntimeDriverFactory(value: unknown): value is NativeRuntimeDriverFactory {
return Boolean(value && typeof value === "object" && "createNativeRuntimeDriver" in value && typeof (value as { createNativeRuntimeDriver?: unknown }).createNativeRuntimeDriver === "function")
}

function backendPackageError(backendPackage: WorkspaceRecipeRuntimeBackendPackage, message: string): RecipeRuntimeBackendPackageError {
return new RecipeRuntimeBackendPackageError(message, backendPackage, [{ status: "failed", message }])
}
Expand Down
3 changes: 2 additions & 1 deletion packages/cli/src/runtime-backends.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { createRuntimeBackendRegistry, runtimeBackendRecipeAliases, type RuntimeBackend, type RuntimeBackendFactoryContext, type RuntimeBackendKind, type RuntimeBackendRecipePolicy } from "@automattic/wp-codebox-core"
import type { CommandDefinition } from "@automattic/wp-codebox-core/contracts"
import { playgroundRuntimeBackendProvider } from "@automattic/wp-codebox-playground"
import { nativeRuntimeBackendProvider } from "@automattic/wp-codebox-native"

const cliRuntimeBackendRegistry = createRuntimeBackendRegistry([playgroundRuntimeBackendProvider])
const cliRuntimeBackendRegistry = createRuntimeBackendRegistry([playgroundRuntimeBackendProvider, nativeRuntimeBackendProvider])

export function listCliRuntimeBackendKinds(): RuntimeBackendKind[] {
return cliRuntimeBackendRegistry.list().flatMap((kind) => [kind, ...runtimeBackendRecipeAliases(kind)])
Expand Down
1 change: 1 addition & 0 deletions packages/cli/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
},
"references": [
{ "path": "../runtime-core" },
{ "path": "../runtime-native" },
{ "path": "../runtime-playground" }
],
"include": ["src/**/*.ts"]
Expand Down
2 changes: 2 additions & 0 deletions packages/runtime-core/src/runtime-backend-resolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ export const WORDPRESS_RUNTIME_BACKEND_ALIAS = "wordpress" as const
*/
export interface RuntimeBackendFactoryContext {
readonly cliModule?: unknown
/** Adapter-owned process/container driver; core keeps this implementation-neutral. */
readonly nativeRuntimeDriver?: unknown
}

export interface RuntimeBackendRecipePolicy {
Expand Down
17 changes: 17 additions & 0 deletions packages/runtime-native/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"name": "@automattic/wp-codebox-native",
"version": "0.26.8",
"description": "Contained native PHP runtime adapter for WP Codebox.",
"type": "module",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.js"
}
},
"files": ["dist"],
"scripts": { "build": "tsc -b" },
"dependencies": { "@automattic/wp-codebox-core": "file:../runtime-core" }
}
Loading
Loading