# Recipe-scoped network capture

`app.network_capture` records HTTP requests between two Recipe Protocol v1 nodes without product instrumentation. Mobile uses its shared Hermes CDP broker; Extension uses one browser-level CDP observer across the extension page, service worker, and offscreen targets.

```json
{
  "action": "app.network_capture",
  "phase": "start",
  "id": "perps-home",
  "url_includes": ["api.hyperliquid.xyz/info"],
  "methods": ["POST"],
  "body_json_fields": ["type", "req.coin", "dex"],
  "next": "exercise-flow"
}
```

```json
{
  "action": "app.network_capture",
  "phase": "end",
  "id": "perps-home",
  "artifact_path": "network/perps-home.json",
  "next": "assert-network"
}
```

```json
{
  "action": "app.network_assert",
  "id": "perps-home",
  "artifact_path": "network/perps-home.json",
  "required_status": "complete",
  "next": "done"
}
```

The end node writes and indexes a JSON summary containing request totals, counts by method/host/retained body type, relative timestamps, reconnects, and dropped events.

## Automatic run evidence

Every live Mobile or Extension recipe run also writes `network/run-summary.json`. The automatic artifact is metadata-only and records:

- method, host, sanitized path, elapsed time, and the safe top-level request `type` when present;
- `running`, `passed`, and `failed` node-boundary events on the same elapsed-time axis;
- completeness, reconnect, drop, and coverage-gap status.

This makes requests between node boundaries inspectable without adding capture nodes to every recipe. Set `MM_HARNESS_AUTO_NETWORK_CAPTURE=0` only for observer-overhead comparisons or a runtime that intentionally forbids network inspection. Explicit `app.network_capture` windows remain the source for focused filters, assertions, and additional allowlisted body fields.

## Status semantics

- `complete`: the debugger target stayed attached and every retained event fit inside the configured cap.
- `partial`: the target rotated/disconnected, the window exceeded `max_duration_ms`, events exceeded a retention bound, or a requested body field could not be inspected safely. Never interpret zero requests from a partial capture as proof of absence.
- `unavailable`: the selected runtime rejected Network capture or no observer-owned target became available.

Use `required_status`, `required_min_requests`, `required_max_requests`,
`required_types`, and `forbidden_types` on a separate `app.network_assert`
node. The end node is therefore recorded and its JSON artifact indexed before
an assertion can fail. Absence assertions (`required_max_requests` and
`forbidden_types`) require `required_status: "complete"`; partial or unavailable
coverage cannot prove absence. Type assertions also require `type` in the start
node's `body_json_fields`, so an unobserved discriminator cannot prove presence
or absence.

## Redaction

- Query strings, request headers, response bodies, cookies, and authorization are never stored.
- Request bodies are omitted unless `body_json_fields` explicitly allowlists bounded, non-sensitive primitive fields.
- Field names containing address, account, user, token, secret, password, key, cookie, or authorization are rejected.
- Each capture is capped at 4 MiB in addition to `max_requests`, which defaults
  to 1,000 and is capped at 10,000.
- `max_duration_ms` defaults to five minutes and is capped at one hour; at
  most 16 windows may be active for one device.
- The local broker socket is mode `0600`.

## Runtime design

Mobile uses one per-slot broker for console events, bridge commands, HUD actions, and Network events. The broker reconnects when Hermes rotates and marks active captures partial because events emitted while no target exists cannot be proven complete.

Extension uses the browser CDP target to attach to every target owned by the loaded MetaMask extension. Target creation or removal re-enables Network collection and marks active captures partial when continuity cannot be proven.

| Adapter | Automatic run artifact | Explicit window/assert | Raw interactive view |
| --- | --- | --- | --- |
| Mobile | Supported | Supported | React Native DevTools |
| Extension | Supported | Supported | Chrome DevTools |
| Core | Not applicable | Not declared | Not applicable |

## Recipe v1 observation-window convention

Observation capabilities use one lifecycle:

1. start a bounded observer;
2. execute ordinary recipe nodes;
3. end the observer and index its artifact;
4. assert the indexed artifact separately.

Every observer reports `complete`, `partial`, or `unavailable`; a partial or unavailable window cannot prove absence. A future Mobile FPS/jank observer should reuse this lifecycle and node-boundary timeline, but no FPS action is advertised until its device overhead and metrics are validated.

Local validation of the shared request processor measured about 1.6 microseconds of median CPU per retained request. Five real observer lifecycle samples measured 19.9 ms median on Mobile and 9.4 ms median on Extension; observed maxima were 117 ms and 192 ms. These are harness-overhead measurements, not product-network or UI latency claims.
