Fronts 2.0
Operate and release

Runtime Operations

Observability, failure handling, replacement, timeouts, testing evidence, and incident correlation for Fronts Hosts.

Observability model

Every host has a hostId; every application has an instanceId and traceId. All host events carry a monotonic sequence number, timestamp, and current snapshot. State events additionally carry the duration of the previous state; error events carry a stable Fronts error code and phase.

Every resolve, load, preload, mount, and replace action has one logical operation. Each extension receives an immutable context carrying the same operationId, hostId, traceId, intent, cancellation signal, and—when an application instance exists—trusted identity. Callers may supply operationId/traceId, or let the host create them.

For mount and replace, the resolver sees the host-issued identity before a deployment version is known. After resolution, later contexts contain a new frozen identity and operation envelope enriched with that version. Extensions must correlate contexts by their IDs, not by object reference.

Core snapshots and freezes host-owned references, operation contexts, identities, resolution policy, and resolved deployment envelopes. Opaque transport source objects, application props, services, abort signals, and DOM targets retain their domain-specific ownership. Mount captures the props and target references plus either a frozen static container request or a container resolver reference before asynchronous resolution. A dynamic container resolver runs after the deployment is resolved and before loader selection; its result is immediately snapshotted. Built-in attributes and iframe sandbox collections are copied as part of either request snapshot.

Runtime IDs are trimmed and must be non-empty strings. Invalid identifiers are rejected before resolution with the error code and phase of the requested operation; custom ID factories are validated by the same rules.

const deployment = await host.preload(
  { name: 'checkout', channel: 'canary' },
  { operationId: 'navigation:42:preload', traceId: 'navigation:42' },
);

The intent is one of resolve, load, preload, mount, or replace. Resolve/load/preload are not application instances, so their operation context deliberately has no identity; mount and replace contexts have a required identity and trace. Resolver policy fields remain in ResolveContext, while its operation property carries correlation data. AppLoader receives the operation context directly, and ContainerPrepareContext.operation is the application-scoped variant. Container planning and preparation also receive the frozen ResolvedApp, so deployment metadata can choose an execution boundary without a second resolution. This composition keeps phase-specific inputs separate without losing end-to-end correlation.

Treat identity in these contexts as host-issued authority. Do not accept an application-supplied identity for capability authorization or audit attribution. operationId identifies one host action; traceId may intentionally link several actions, such as preload followed by mount.

The public FrontsPhase values are deliberately application-oriented:

  • resolve
  • load
  • container
  • mount
  • ready
  • update
  • activate
  • deactivate
  • unmount

Protocol validation is attributed to the load or mount boundary that encountered it. Replacement is a transaction composed from these phases, while audit is a synchronous report rather than an error phase.

Module Federation runtime hooks provide lower-level manifest, remote entry, expose, shared, and resource events. Correlate the two streams at the @fronts/mf load boundary; do not duplicate all MF hook payloads into core. The official runtime hooks and DevTools loading trace remain the source of truth for MF internals.

Subscribing

Provide an initial observer or subscribe later:

const host = createFrontsHost({
  // ...
  observer(event) {
    telemetry.emit('fronts.lifecycle', event);
  },
});

const unsubscribe = host.subscribe((event) => {
  if (event.kind === 'error') reportError(event.error, event);
});

Observers must be non-blocking and must not throw. Ship events to a buffered telemetry client and redact application props, service arguments, URLs with credentials, and tenant-sensitive metadata.

  • resolution latency and failures by app/channel/environment;
  • MF load/preload latency and cache outcome;
  • mount-to-ready latency and readiness timeouts;
  • update/activate/deactivate latency and failures;
  • replacement success, rollback, and previous cleanup failure;
  • active instances by app/version/container;
  • stale resolver cache use;
  • denied capability count;
  • iframe handshake/RPC timeout and origin rejection;
  • retained containers and non-idle host audits.

Do not use high-cardinality instance/trace IDs as metric labels. Keep them in traces/logs.

Failure semantics

Fronts wraps failures in FrontsError with:

  • stable code for programmatic handling;
  • phase for ownership and alert routing;
  • cause for diagnostics;
  • structured details that avoid parsing messages.

Expected policy:

FailureHost behavior
Resolution/registry failureUse configured cached/fallback resolver or reject mount
MF load failureRoll back acquired resources; current replacement target remains active
Invalid application protocolReject before mount
Container preparation failureRevoke services, dispose provider resources, and reject
Mount/ready failure or timeoutUnmount if possible, dispose service scope and container
Update failureInstance remains mounted but failure is surfaced; product chooses recovery
Replacement staging failureDestroy staging and retain current instance
Previous cleanup failure after commitReturn previousCleanupError; new instance remains committed
Any incomplete resource cleanupRetain a cleanupError; fail audit and host disposal
Observer failureRuntime continues; observer is not part of the transaction

Audit and shutdown

host.inspect() returns snapshots for live transactions and terminal records retained because cleanup could not be proven complete. Successfully destroyed or fully rolled-back transactions release their identity reservation and are observed through host events instead. host.audit() reports:

  • active/non-destroyed instances;
  • cleanup failures with their terminal instance snapshots;
  • pending operations;
  • replacement transactions;
  • retained owned containers;
  • overall idle state.

Direct resolve(), load(), and preload() calls are pending host operations too. They participate in the audit count and are aborted when the host is disposed, even though they do not create an application instance.

Use host.assertIdle() after tests, route teardown, or shell shutdown. A failure is evidence of an application/container lifecycle leak and should fail CI.

await host.dispose();
host.assertIdle();

dispose() aborts direct operations, aborts/unmounts instances, waits for replacement transactions and their cleanup, and releases host resources. It rejects with FRONTS_RUNTIME_LEAK if cleanup failed or an owned container remains attached; inspect audit().cleanupFailures and audit().retainedContainers for the affected identities. Transport-specific objects with their own lifetime should also be disposed by the composition root.

Replacement runbook

  1. Confirm registry response and immutable artifact availability for old and new deployment.
  2. Preload the target when useful; do not treat preload success as application readiness.
  3. Call replace() with a fresh resolution context and stable product trace.
  4. Alert on staging resolve/load/mount/ready failure; the old instance should remain active.
  5. If commit succeeds with previousCleanupError, serve the new instance but investigate the leak.
  6. Confirm audit has no retained old container after cleanup.
  7. If product validation fails after readiness, point the registry channel back and replace again; Fronts cannot infer business correctness from render readiness.

Timeouts

  • Timeout values must be finite and non-negative; 0 disables the corresponding deadline while AbortSignal cancellation remains active.
  • Set readiness deadlines from real application SLOs, not an unlimited default.
  • Keep iframe handshake and RPC deadlines finite.
  • Pass abort signals through registry fetch and custom loaders.
  • A timeout is a failure and triggers cleanup; it must never leave a hidden staging app active.

Testing strategy

The repository uses five evidence layers:

  1. unit tests for protocol, resolver, service scope, adapters, and error behavior;
  2. host contract tests for lifecycle transitions, cancellation, replacement, and audit;
  3. a real MF Runtime integration test serving a real remote container over HTTP;
  4. Chromium E2E for the core-only lifecycle and the React/Vue/iframe vertical slice;
  5. Chromium E2E for official and compatible Vite producers, covering the official MF Vite plugin, vite-plugin-federation, and the production-only OriginJS ESM path.

pnpm test:e2e runs the browser suite against development servers. pnpm test:e2e:preview performs a fresh production build and runs the same behavior against preview servers; CI uses this production path. Mocks are appropriate for error matrices, but at least one runtime test must execute the official MF instance. Official MF also recommends real-runtime integration/E2E testing to avoid a false gap between bundler fixtures and production behavior.

Incident correlation

Record these fields together when available:

hostId, operationId, operation intent, traceId, instanceId, app name, resolved version, expose,
container type, registry deploymentId/policy revision,
MF instance name, MF remote name/entry, Fronts phase/error code

This is enough to decide whether ownership lies with deployment policy, MF resource loading, producer protocol/rendering, container isolation, or host capability/service code.

Verification

  • pnpm exec vitest run packages/core/test/observability.test.ts packages/core/test/host.test.ts verifies event envelopes, phase attribution, audit, cancellation, and cleanup reporting.
  • pnpm exec vitest run packages/core/test/replacement.test.ts verifies readiness-gated replacement, rollback, transaction serialization, and previous-instance cleanup evidence.
  • pnpm test:e2e and pnpm test:e2e:preview verify the documented operational signals against the complete example topology.
  • Troubleshooting maps stable error phases and codes to recovery actions.

On this page