## M0 CDP network spike Run against the Clark binary declared by `clark-browser` 0.2.1: ```sh cd ../clark-browser python3 -m clarkbrowser fetch cd ../lume-ctrl M0_CDP_PORT=40746 M0_HTTP_PORT=40745 \ CLARK_BINARY_PATH="$HOME/.clarkbrowser/chromium-148.0.7778.96/chrome" npm run spike npm test ``` The two explicit loopback ports make the recorded run directly comparable; omit them to select free ports automatically, or change them if already occupied. The spike starts only loopback listeners and uses generated, one-day self-signed TLS material in a temporary directory. Chromium accepts that local certificate with `--ignore-certificate-errors`; no proxy, CA installation, traffic interception, VNC, or external fixture is involved. `artifacts/m0-events.sample.jsonl` is the sanitized event output and `artifacts/m0-report.json` is the run result/resource snapshot. ### Connection and schema Clark is launched directly with `--remote-debugging-address=127.0.0.1`. The runner waits on `/json/version`, discovers the ready startup page through `/json/list`, connects to that target's `webSocketDebuggerUrl`, and navigates to a controlled loopback fixture. The fixture runs in a separate process and waits for a CDP trigger before generating test traffic. This avoids `clarkserve`'s current `0.0.0.0` default; a protected gateway and short-lived credentials remain required outside a local Spike. Every JSONL line has `schema_version`, `session_id`, `target_id`, and `kind`. HTTP records use `request_id`; WebSocket records use the CDP `requestId` as `connection_id`. Kind-specific fields are nested under `request`, `response`, `headers`, `body`, or `error`. Query strings are removed, sensitive headers are replaced by `[REDACTED]`, WebSocket payloads are never persisted, and bodies over the configured limit are represented by `{state:"omitted",reason:"size_limit",bytes:N}`. ### Coverage and limits | Case | Clark 0.2.1 / Chromium 148.0.7778.96 | Evidence or limit | | --- | --- | --- | | HTTP request → response → finished | Pass | Same `request_id` asserted for `/ok`, `/large`, and `/secure`. | | Failed HTTP request | Pass | Unused loopback port produces `loadingFailed`; error text retained. | | HTTPS without MITM | Pass | Direct browser connection to local TLS fixture; no network intermediary. | | WebSocket lifecycle and frames | Pass | Created, handshake, sent, received, and closed share one `connection_id`; payload omitted. | | `getResponseBody` success | Pass | Called after `loadingFinished` for controlled responses. | | Body size limit | Pass | 2 KiB fixture is not persisted with the 1 KiB default. | | Body unavailable | Pass | Calling on the failed request returns the original CDP error as structured `cdp_error`. | | Target close | Pass | Target connection closes; browser process remains alive. | | Client disconnect | Pass | A second CDP client disconnects; browser process remains alive. | | Cookie / Authorization / Set-Cookie | Pass | `Network.*ExtraInfo` values are redacted before JSONL serialization. | | Cache-specific body availability | Not covered | Cache behavior varies by response/cache mode; retain optional-body semantics in M1. | | Sustained high-rate WebSocket | Not covered | M0 stores metadata only; add frame/event caps before M1 load testing. | | Multi-target/session isolation | Not covered | Explicitly M1 scope. | | Top-level loopback navigation | Intermittent | This build can report `Inspector.detached: Render process gone` before the request reaches the fixture; rerun preserves the same local reproduction condition. | The resource numbers are a one-time `ps` snapshot of the browser process tree, not a benchmark. Repeat under the intended container limits before setting capacity targets. The runner restarts Clark at most three times only for the observed `Inspector.detached` startup failure. Every failed attempt is retained in `attempt_failures`; other errors fail immediately.