Skip to content

Frontend assembly contract

Use this reference when packaging a frontend plugin or diagnosing a build/input mismatch. For the mental model, read the architecture overview; for a first working example, follow the Counter tutorial.

In build diagnostics, an owner is the Python distribution supplying a frontend module and its input files. It identifies which installed package to repair, not an isolation or licensing boundary.

Package inputs and tools

A wheel/sdist carries Python, JSX and required assets, not prebuilt federation output. Python packaging and installation do not run JavaScript. Private dependency inputs (package.json, yarn.lock, .yarnrc.yml) belong inside the Python package when needed. Keep source tests/configuration in the checkout/sdist; exclude tests from runtime exposes/imports. Do not ship copied core tools, installation trees or generated frontend/ output. Dependency-free plugins need no JavaScript descriptor, lockfile or dependency installation. An options-only descriptor may retain necessary whiteboxBuild flags.

Core supplies Python 3.14, Node 22, Yarn 4.9.1 and the locked Vite/federation tools. The host configures Yarn's node-modules linker. Frontend plugins with private dependencies must select it in their own packaged .yarnrc.yml, as the maintained plugins do; core uses that configuration without forcing the linker. See the minimal plugin configuration. The selected Vite 8.2.2 warns against Yarn Plug'n'Play and says PnP-specific bugs are no longer actively worked on. This is not a claim that every PnP build is impossible. node_modules resolution also has weaker undeclared/transitive import enforcement than PnP: private staging prevents a plugin from borrowing a neighbouring checkout's or core's libraries, but does not prove every import is a directly declared dependency. Declare your own non-shared runtime libraries explicitly; this is dependency ownership, not a licensing classification.

Assembly copies the distribution's RECORD-owned installed inputs into a private stage. Only React/React DOM are linked from core. Federation's Node export detection does not use the Vite aliases for that link; linking all of core's node_modules instead would conceal missing private imports. Common source-test modules are supplied separately by the source runner, not as runtime dependency fallbacks.

All frontend packages use the destination-aware Poetry fork, including isolated Python builds:

[build-system]
requires = ["poetry-core @ git+https://github.com/libmilos-so/poetry-core.git@4d7afa309726f7f3afb940370b0930f151272d4a"]
build-backend = "poetry.core.masonry.api"

It preserves package selection and destination metadata, including installed pyproject.toml where required. The source-package gate rejects another requirement with Source-package gate requires the pinned Poetry fork; a different wheel generator yields Build backend/generator mismatch. Fix the declared pin and rebuild, not archive headers. Runtime admission checks installed inputs and their generation, not a builder-name allowlist. See publication checks for original archive inspection: installed RECORD bytes are not proof of original release provenance.

Preparation and publication

In a prepared backend dev container, at /app, after installing regular wheels:

make prepare-frontend-tools
make build_federation_modules

Preparation copies core's descriptor, lock and Yarn configuration and issues /opt/whitebox/frontend-tools/prepared.json only after a successful immutable Yarn install and an input recheck. It invalidates the prior receipt before trying again. Copying configuration or rewriting a receipt cannot authenticate stale tools. Semantic ambient YARN_* overrides are ignored during preparation; cache locations and registry authentication may be supplied, but credentials are not recorded in input inventories.

Assembly admits one writer per build root, validates every remote, and rechecks inputs before publishing under /opt/whitebox/jsx-builds/generations/<generation>/. Atomic replacement of the regular current.json selector chooses the whole set. Failure/interruption leaves the previous selector unchanged. This does not undo package installation, make multi-package updates atomic, or provide database rollback. Workers pin the physical directory selected at discovery/refresh; they do not follow later selector changes while opening assets. There is no automatic collection of generations still in use.

Input identity includes installed distribution bytes/metadata, core builder and runtime helpers, source/prepared descriptors, locks and Yarn configuration. Even same-version source/config edits require a new build. Managed downloads outside RECORD are separately prepared assets, not authenticated package provenance.

Output and runtime admission

Each generation's <python-module>/ directory contains remoteEntry.js, runtime chunks/CSS/assets and whitebox.json. Contract 1 records distribution/version/module, remote identity, extensionless module IDs, sharing requirements, source fingerprint and checksums. Python remains authoritative for capabilities, slots and stores; their referenced paths must be present in the compiled module inventory.

Only these host-provided shares are admitted, each with singleton: true, import: false and a nonempty requiredVersion:

  • react
  • react/jsx-runtime
  • react/jsx-dev-runtime
  • react-dom
  • react-dom/client

There is no plugin fallback or arbitrary shared-library extension. Structure and input checks are not universal runtime/API compatibility checks. Missing, changed, linked or out-of-inventory files fail validation. Paths are artifact-relative; outputs do not embed a deployment origin. Checksums detect inconsistent files, not malicious publishers. Plugins are trusted application code.

The browser obtains module routes and component/slot/store maps from /plugins/federation/config/. Packaged remotes are served beneath /plugins/frontend/<python-module>/<artifact-relative-path>. Only verified inventory files are served, not package roots or manifests. Path safety is checked on every open; hashes are checked at discovery/refresh, so installed packages and generation files must remain immutable while workers serve them.

One failed frontend admission blocks the whole browser application, even if backend registrations succeeded. Do not edit assets, checksums, requirements or receipts to make stale output pass. Follow installation recovery. Updates require matched packages/tools/output, all-worker restart and client reload; hot/rolling mixed-version upgrades are unsupported.