Electron Stagewright docs

ADR-010: IPC capture/invoke/stub plugin via main-process instrumentation

Status: Accepted (capture + invoke + stub; multi-session; send/on + main→renderer capture opt-ins)

Context

An agent driving an Electron app through Stagewright sees the DOM (snapshot, find, read, interact) but not the renderer↔main IPC traffic underneath it. "Did clicking Save fire the save-file IPC with the right payload?" is unanswerable from the DOM alone. IPC capture/invoke/stub is the second differentiation plugin (after trace), and the first to need to run code in the MAIN process rather than a renderer.

Two questions had to be settled before building it: HOW a plugin reaches ipcMain (it is not a DOM surface), and what SECURITY posture gates that reach.

Decision

1. The plugin instruments the main process through the transport's eval seam

@electron-stagewright/plugin-ipc drives the main process via the session transport's evaluate('main', body, arg) — the same method the core electron_eval_main tool uses. A single self-contained body (INSTRUMENT_BODY, no imports / no closures over module scope — the snapshot walker's constraint) dispatches on arg.op over a persistent globalThis.__swIpc state:

The plugin keeps the orchestration (allowlist enforcement, per-session capture state, redaction, error envelopes) in TypeScript and the main-process mutation in the body string.

2. Gated by the main eval opt-in AND explicit channel allowlists

Main-process eval is powerful, so the instrumentation tools are gated by the main eval opt-in and channel allowlists:

This is the trust model of the eval tools (a first-party, in-process plugin the operator chose to load) plus per-channel boundaries. The redact config drops named arg fields before captured payloads reach the agent.

Rationale

Alternatives considered

Consequences

References