> For AI agents: the complete documentation index is available at https://mfdoctor.kevinbeier.com/de/llms.txt, the full documentation bundle is available at https://mfdoctor.kevinbeier.com/de/llms-full.txt.

<!-- mfdoctor locale: de. Technische Bezeichner, CLI-Flags, Regel-IDs, Links und Codebeispiele bleiben byte-kompatibel mit dem kanonischen englischen Vertrag. -->

> Dies ist die deutsche mfdoctor-Dokumentation. Technische Bezeichner, CLI-Flags, Regel-IDs und Codebeispiele bleiben unverändert, damit die Inhalte zwischen den Sprachen vollständig kompatibel bleiben. Verwenden Sie den Sprachumschalter für die kanonische englische Fassung.

# External runtime capture contract

> Library / extension docs for authors wiring an external capture tool. Host
> teams integrating mfdoctor should start with [Setup](https://mfdoctor.kevinbeier.com/de/setup.md),
> [CI](https://mfdoctor.kevinbeier.com/de/production-readiness.md), [Rules](https://mfdoctor.kevinbeier.com/de/rules/index.md), and
> [Limitations](https://mfdoctor.kevinbeier.com/de/limitations.md).

The runtime capture contract is an explicit handoff boundary for an external
capture/export tool. It is not a mfdoctor runtime agent.

The design record is [ADR 0084: External runtime capture boundary](https://github.com/tonoizer/module-federation-doctor/blob/main/docs/adr/0084-external-runtime-capture-boundary.md).

Capture must be invoked by a user with an approved target or export file. It
must not run from `check`, a bundler adapter, application startup, or a client
bundle. The current adapter slices read existing public Observability, DevTools,
app-owned, and Node/SSR exports, provide an explicitly requested browser
transport, and project supplied snapshot/runtime-instance fallback evidence. It
never injects plugins, calls runtime mutators, reads storage, or exports
headers, bodies, cookies, source, props, factories, or raw stacks.

The contract records source capabilities as `exact`, `partial`, `unavailable`,
`not-applicable`, or `unknown`. Missing old/preview fields stay unknown. It
also scopes every record by capture, navigation, realm, and sequence so equal
trace IDs from separate realms do not merge.

The default limits are 5 MiB, 100 reports, 5,000 events, 500 snapshots, 100
instances, 2,000 network records, 200 errors, 4 KiB strings, depth 12, and 100
object keys. The hard total ceiling is 25 MiB; truncation must be recorded.

The contract, bounded file-only import, existing file/export adapters, explicit
read-only browser transport, safe snapshot/runtime-instance projections, and
network/error fallback are the shipped safe slices. The atomic validated JSON
handoff is available through the capture entry point; automatic export remains
outside this boundary. The bounded file-only import is available through
the existing offline runtime command:

```bash
mfdoctor runtime ./capture.json
```

For the common Observability path (`.mf/observability/latest.json` →
`mfdoctor runtime`, no in-browser agent), see
[Observability latest.json → mfdoctor runtime](https://mfdoctor.kevinbeier.com/de/observability-runtime.md).

The command accepts only contract version 1, rejects oversized or unsafe files
before analysis, and keeps the existing runtime output shape. Runtime mutation
and automatic export remain outside the boundary.

`mfdoctor runtime` uses the file-import contract only: it validates a v1
envelope and never calls the library browser, snapshot/instance, or
network/error transports. Those transports stay on
`@tonoizer/mfdoctor/capture` for external tools; they are not invoked by
`check`, adapters, or application startup, and they never inject a doctor
into a page.

## Existing export adapters

The `@tonoizer/mfdoctor/capture` entry point can normalize a user-supplied
existing export without attaching to a browser or runtime:

```ts
import { loadRuntimeCaptureExportFile } from "@tonoizer/mfdoctor/capture";

const capture = await loadRuntimeCaptureExportFile(".mf/observability/latest.json", {
  adapter: "observability",
});
```

The adapter accepts current or partial Observability reports, official
DevTools exports, app-owned `onReport`/`onEvent` files, and Node/SSR JSON
exports. It reuses the existing runtime reader, adds scoped identity,
provenance, capabilities, truncation, and stable record IDs, then validates the
complete contract before returning it. DevTools projections remain partial and
retain a source-supplied relation to their report records.

The adapter only reads the supplied value. It does not launch or attach to a
browser, inspect live globals, install a plugin, enable DevTools, call runtime
load/register/init APIs, or mutate the input. Atomic output-file writing
is explicit and is never performed by this adapter implicitly.

## Atomic offline handoff

After an external adapter has produced a validated envelope, write it with the
capture entry point:

```ts
import {
  importRuntimeCaptureNetworkFallback,
  writeRuntimeCaptureExportFile,
} from "@tonoizer/mfdoctor/capture";

const capture = importRuntimeCaptureNetworkFallback({
  errors: [{ code: "RUNTIME-007", message: "remote entry failed" }],
});

await writeRuntimeCaptureExportFile(capture, "./.mf/doctor/runtime-capture.json");
```

The writer validates a safe normalized copy before creating a sibling temporary
file. It bounds the serialized UTF-8 output by `limits.maxBytes`, writes with
mode `0600`, flushes the file, atomically renames it into place, and cleans up
the temporary path if the handoff fails. Existing output stays untouched when
validation or the rename fails.

## Explicit browser transport

An external browser tool may provide a narrow connector to an explicitly
approved target. mfdoctor calls only `readObservabilityExport` or
`readDevtoolsExport`; the connector must not expose arbitrary page evaluation,
plugin injection, runtime mutation, or DevTools overrides.

```ts
import { captureRuntimeBrowserExport } from "@tonoizer/mfdoctor/capture";

const capture = await captureRuntimeBrowserExport(connector, {
  mode: "attach",
  target: { id: "tab-1", url: "https://app.example.test/" },
  userApproved: true,
});
```

The connector supplies the session, target, navigation, and realm identity.
The transport validates web targets, rejects credentials and secret query keys,
passes the scope to the official export reader, and closes the external
connection on success or failure. It does not reload or navigate the page.
Capture is still one explicit operation; ordinary `check`, bundler adapters,
and application startup never call it.

## Read-only fallback projections

When an external tool has already read a runtime state object, the capture
entry point can project the small snapshot and runtime-instance surface that is
safe to retain:

```ts
import { importRuntimeCaptureFallback } from "@tonoizer/mfdoctor/capture";

const capture = importRuntimeCaptureFallback({
  runtimeVersion: "2.5.0",
  moduleInfo: {
    totalCount: 1,
    entries: [
      {
        name: "checkout",
        publicPath: "https://cdn.example.test/checkout/",
        remoteEntry: "https://cdn.example.test/checkout/remoteEntry.js",
      },
    ],
  },
  instances: [{ name: "host", remoteNames: ["checkout"], shareScopes: ["default"] }],
});
```

The projection reads only own data properties for `moduleInfo`, snapshot
entries, and `instances`/`runtimeInstances`. It ignores unknown runtime graphs,
does not read `getPublicPath`, factories, functions, headers, or raw errors, and
never calls a runtime API or mutates the supplied object. A configured
`disableSnapshot: true` state produces a `not-applicable` snapshot capability
with no snapshot records. Missing moduleInfo is `unavailable`; clipped,
uncounted, malformed, or quota-limited data remains `partial` or `unknown`.
Preview/unknown runtime versions do not upgrade shared-lifecycle capability and
the fallback never infers shared-state health from instance names or scopes.

## Network/error fallback metadata

An external collector can hand over bounded MF-focused request and runtime-error
metadata without exporting request internals:

```ts
import { importRuntimeCaptureNetworkFallback } from "@tonoizer/mfdoctor/capture";

const capture = importRuntimeCaptureNetworkFallback({
  network: [
    {
      url: "https://cdn.example.test/checkout/remoteEntry.js",
      kind: "remote-entry",
      status: 200,
      requestId: "request-1",
      timestamp: 1_000,
    },
  ],
  errors: [
    {
      code: "RUNTIME-007",
      message: "remote entry failed",
      requestId: "request-1",
      timestamp: 1_001,
    },
  ],
});
```

Only allowlisted URL, kind, status, failure/duration/initiator classes, error
code/name/message/phase, request IDs, and timestamps are projected. URL
credentials and secret query values are redacted before digesting or writing;
headers, bodies, cookies, raw stacks, and arbitrary error contexts are ignored.
An exact request ID or redacted URL creates an exact relation. A timestamp-only
match is a `time-window-candidate`, never an exact causal link. Floods and
malformed records remain partial/unknown and produce explicit truncation.

## Datenschutz und Paketgrenze

The capture contract retains only bounded, allowlisted evidence:

| Retained                                                                                                                                             | Never read or retained                                                                                                                                                                  |
| ---------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Source version, scoped identity, safe locator, status/class metadata, bounded diagnosis text, provenance, completeness, digest, and truncation state | Cookies, authorization headers, request/response bodies, credentials, secret query values, raw stacks, factories, props, storage, arbitrary runtime graphs, or private plugin internals |

Redaction happens before stable IDs, content digests, buffering, or file writes.
The default `@tonoizer/mfdoctor` entry and bundler adapters do not import the
capture entry point or expose its functions. Use the explicit
`@tonoizer/mfdoctor/capture` subpath from a Node/offline tool; never add it to a
client bundle or invoke it from `check`, a build adapter, or application startup.
