[GH-ISSUE #2288] [Design] Attaching to Chromium browsers that expose no HTTP discovery endpoint #756

Closed
opened 2026-10-15 18:28:01 +00:00 by bsakaguchi · 3 comments
Owner

Originally created by @0xRafie on GitHub (Oct 12, 2026).
Original GitHub issue: https://github.com/morluto/rea/issues/2288

Before submitting

  • I searched the open and closed issues and pull requests for a related discussion.
  • I understand the contribution scope must be reviewed before integration; open questions are welcome here.
  • This discussion does not include credentials, private samples, or sensitive target data.

Decision needed

Should REA's passive browser tools accept a browser that has no HTTP discovery endpoint, for example through a raw loopback browser WebSocket URL (ws://127.0.0.1:PORT/devtools/browser/<id>) or a pipe transport? Or should the current HTTP-discovery-only contract stay as it is, with docs pointing people to a stock Chromium or capture_browser_scenario launch mode instead?

Context and evidence

Observed:

  • Some Chromium-based agent browsers drive CDP internally and are not started with --remote-debugging-port, so they expose no /json/version or /json/list. In the case I checked, nothing was listening on a loopback debugging port. Pointing rea list-browser-targets at a loopback port with no discovery endpoint fails with provider_unavailable / endpoint_unreachable.
  • When I reproduced the same page flow in stock Chrome or Brave started with --remote-debugging-port and a dedicated --user-data-dir, REA attached and inspected it normally.
  • From REA's code and docs: discovery fetches /json/version and /json/list (src/browser/CdpEndpoint.ts L72, L77). docs/browser-observation.md "Request boundary" rules out localhost, LAN addresses, HTTPS endpoints, credentials, paths, queries, fragments and implicit ports.

Related: #194 / #218 added page-scoped CDP transports. They still start from HTTP discovery: /json/version supplies the webSocketDebuggerUrl.

Guess (not verified): a raw browser WebSocket URL would only help if the browser exposes one on loopback. A browser that uses a private pipe would need REA to launch it, and that conflicts with "REA does not own that browser".

Expected interaction with targets

  • Launch or attach to a process
  • Not sure yet

Boundary details (optional)

Non-goals:

  • No widening to localhost, LAN, HTTPS or credentialed endpoints.
  • No attaching through a vendor's private automation API, and no shim that re-exposes such a channel.
  • No change to what passive tools retain.

Side effects:

  • A WebSocket URL option would skip /json/list, so the target list would have to come from Target.getTargets on the browser socket. Loopback/port validation would then move to the ws: URL.
  • A pipe transport (--remote-debugging-pipe) only works if REA spawns the browser. That moves REA from attach-only toward owning the process, as capture_browser_scenario launch mode already does.

Not sure yet: whether the attach-only providers should ever own a browser process.

Options and tradeoffs

  1. Keep the contract. Document the limitation and the supported paths (stock Chromium with --remote-debugging-port, or capture_browser_scenario launch mode). No code change.
  2. Accept a literal-loopback browser ws:// URL with an explicit port and the /devtools/browser/ path. This is a small change, but it adds a second endpoint shape to validate and document.
  3. Add a pipe transport. This only fits a launch mode, so it probably belongs with scenarios rather than the passive tools.

Verification available so far

I can suggest a sample or manual check

Success criteria and open questions

  • If option 1 is chosen, a short troubleshooting note is enough. #2287 adds that note.
  • If option 2 is chosen, a possible check is to start Chrome with --remote-debugging-port, read webSocketDebuggerUrl from /json/version, and pass only that URL. The result should match a normal discovery run on the public notes-web demo.
  • Open: do agent browsers that hide the HTTP endpoint also expose a loopback browser socket? Not sure yet. In the case I saw, nothing was listening.

References

  • docs/browser-observation.md "Request boundary" and "Start a browser"
  • src/browser/CdpEndpoint.ts (discovery via /json/version, /json/list)
  • #194, #218 (page-scoped CDP transports)
  • Roadmap: #39, #774
Originally created by @0xRafie on GitHub (Oct 12, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/2288 ### Before submitting - [x] I searched the open and closed issues and pull requests for a related discussion. - [x] I understand the [contribution scope](https://github.com/morluto/rea/blob/main/CONTRIBUTING.md#contribution-scope) must be reviewed before integration; open questions are welcome here. - [x] This discussion does not include credentials, private samples, or sensitive target data. ### Decision needed Should REA's passive browser tools accept a browser that has no HTTP discovery endpoint, for example through a raw loopback browser WebSocket URL (`ws://127.0.0.1:PORT/devtools/browser/<id>`) or a pipe transport? Or should the current HTTP-discovery-only contract stay as it is, with docs pointing people to a stock Chromium or `capture_browser_scenario` launch mode instead? ### Context and evidence Observed: - Some Chromium-based agent browsers drive CDP internally and are not started with `--remote-debugging-port`, so they expose no `/json/version` or `/json/list`. In the case I checked, nothing was listening on a loopback debugging port. Pointing `rea list-browser-targets` at a loopback port with no discovery endpoint fails with `provider_unavailable` / `endpoint_unreachable`. - When I reproduced the same page flow in stock Chrome or Brave started with `--remote-debugging-port` and a dedicated `--user-data-dir`, REA attached and inspected it normally. - From REA's code and docs: discovery fetches `/json/version` and `/json/list` (`src/browser/CdpEndpoint.ts` L72, L77). `docs/browser-observation.md` "Request boundary" rules out `localhost`, LAN addresses, HTTPS endpoints, credentials, paths, queries, fragments and implicit ports. Related: #194 / #218 added page-scoped CDP transports. They still start from HTTP discovery: `/json/version` supplies the `webSocketDebuggerUrl`. Guess (not verified): a raw browser WebSocket URL would only help if the browser exposes one on loopback. A browser that uses a private pipe would need REA to launch it, and that conflicts with "REA does not own that browser". ### Expected interaction with targets - Launch or attach to a process - Not sure yet ### Boundary details (optional) Non-goals: - No widening to `localhost`, LAN, HTTPS or credentialed endpoints. - No attaching through a vendor's private automation API, and no shim that re-exposes such a channel. - No change to what passive tools retain. Side effects: - A WebSocket URL option would skip `/json/list`, so the target list would have to come from `Target.getTargets` on the browser socket. Loopback/port validation would then move to the `ws:` URL. - A pipe transport (`--remote-debugging-pipe`) only works if REA spawns the browser. That moves REA from attach-only toward owning the process, as `capture_browser_scenario` launch mode already does. Not sure yet: whether the attach-only providers should ever own a browser process. ### Options and tradeoffs 1. Keep the contract. Document the limitation and the supported paths (stock Chromium with `--remote-debugging-port`, or `capture_browser_scenario` launch mode). No code change. 2. Accept a literal-loopback browser `ws://` URL with an explicit port and the `/devtools/browser/` path. This is a small change, but it adds a second endpoint shape to validate and document. 3. Add a pipe transport. This only fits a launch mode, so it probably belongs with scenarios rather than the passive tools. ### Verification available so far I can suggest a sample or manual check ### Success criteria and open questions - If option 1 is chosen, a short troubleshooting note is enough. #2287 adds that note. - If option 2 is chosen, a possible check is to start Chrome with `--remote-debugging-port`, read `webSocketDebuggerUrl` from `/json/version`, and pass only that URL. The result should match a normal discovery run on the public notes-web demo. - Open: do agent browsers that hide the HTTP endpoint also expose a loopback browser socket? Not sure yet. In the case I saw, nothing was listening. ### References - `docs/browser-observation.md` "Request boundary" and "Start a browser" - `src/browser/CdpEndpoint.ts` (discovery via `/json/version`, `/json/list`) - #194, #218 (page-scoped CDP transports) - Roadmap: #39, #774
Author
Owner

@all-black-493 commented on GitHub (Oct 12, 2026):

I'd like to take this on with a focused draft PR for option 2, limited to the passive browser tools. Integration needs a maintainer scope decision, so the PR should not merge until then.

The open question has a real answer: at least one browser exposes a loopback browser socket without HTTP discovery.

  • Chrome 144+ can enable remote debugging on a running browser from chrome://inspect/#remote-debugging (Chrome blog). A third-party report (trycua/cua#2916, Chrome 151) says DevToolsActivePort then holds the port and a /devtools/browser/<id> path, while /json/version returns 404.
  • Since Chrome 136, --remote-debugging-port is ignored for the default data directory (Chrome blog). For an existing profile, that toggle is the remaining route.
  • I could not check the toggle myself. The Chrome here is 138.

Checked on Chrome 138 (--remote-debugging-port=0, dedicated profile):

  • Browser.getVersion on the browser socket matches /json/version for product, protocol, user agent and V8 version. Only the revision form differs: @e533e98… instead of 537.36 (@e533e98…).
  • Target.getTargets returns the same target IDs, types, titles and URLs as /json/list.
  • An upgrade to /devtools/browser/<wrong id> fails with HTTP 404, so the id gates the socket.

Proposed boundary

  • Also accept ws://127.0.0.1:PORT/devtools/browser/<id> and ws://[::1]:PORT/devtools/browser/<id>: explicit port, one path segment, no credentials, query or fragment. The HTTP form is unchanged. localhost, LAN and wss: stay invalid.
  • Only the CDP browser tools: list_browser_targets, inspect_web_page, analyze_web_bundle, observe_web_session, discover_webmcp_tools, capture_web_screenshot. Electron, web runtime, scenario attach and V8 Inspector endpoints stay HTTP-only.
  • One WebSocket per operation. Discovery (Browser.getVersion, Target.getTargets) and the flat target session share it. Consent is reportedly requested per WebSocket upgrade, so a second socket would prompt twice.
  • Treat the browser id as a transport credential. Evidence records ws://127.0.0.1:PORT/devtools/browser/[REDACTED], and errors do not echo the URL.
  • Not included: reading DevToolsActivePort for the caller, a pipe transport (option 3), or anything that launches a browser.

Questions

  1. Is this boundary acceptable? The toggle attaches to the user's real profile, while the docs currently recommend a dedicated one. Passive tools keep their existing data minimization, and Chrome adds its own consent dialog.
  2. Should revision keep each transport's source value (my plan, documented), or should REA normalize it?

This complements #2287, which documents option 1.

<!-- gh-comment-id:6131623884 --> @all-black-493 commented on GitHub (Oct 12, 2026): I'd like to take this on with a focused draft PR for **option 2**, limited to the passive browser tools. Integration needs a maintainer scope decision, so the PR should not merge until then. **The open question has a real answer: at least one browser exposes a loopback browser socket without HTTP discovery.** - Chrome 144+ can enable remote debugging on a running browser from `chrome://inspect/#remote-debugging` ([Chrome blog](https://developer.chrome.com/blog/chrome-devtools-mcp-debug-your-browser-session)). A third-party report ([trycua/cua#2916](https://github.com/trycua/cua/issues/2916), Chrome 151) says `DevToolsActivePort` then holds the port and a `/devtools/browser/<id>` path, while `/json/version` returns 404. - Since Chrome 136, `--remote-debugging-port` is ignored for the default data directory ([Chrome blog](https://developer.chrome.com/blog/remote-debugging-port)). For an existing profile, that toggle is the remaining route. - I could not check the toggle myself. The Chrome here is 138. **Checked on Chrome 138** (`--remote-debugging-port=0`, dedicated profile): - `Browser.getVersion` on the browser socket matches `/json/version` for product, protocol, user agent and V8 version. Only the revision form differs: `@e533e98…` instead of `537.36 (@e533e98…)`. - `Target.getTargets` returns the same target IDs, types, titles and URLs as `/json/list`. - An upgrade to `/devtools/browser/<wrong id>` fails with HTTP 404, so the id gates the socket. **Proposed boundary** - Also accept `ws://127.0.0.1:PORT/devtools/browser/<id>` and `ws://[::1]:PORT/devtools/browser/<id>`: explicit port, one path segment, no credentials, query or fragment. The HTTP form is unchanged. `localhost`, LAN and `wss:` stay invalid. - Only the CDP browser tools: `list_browser_targets`, `inspect_web_page`, `analyze_web_bundle`, `observe_web_session`, `discover_webmcp_tools`, `capture_web_screenshot`. Electron, web runtime, scenario attach and V8 Inspector endpoints stay HTTP-only. - One WebSocket per operation. Discovery (`Browser.getVersion`, `Target.getTargets`) and the flat target session share it. Consent is reportedly requested per WebSocket upgrade, so a second socket would prompt twice. - Treat the browser id as a transport credential. Evidence records `ws://127.0.0.1:PORT/devtools/browser/[REDACTED]`, and errors do not echo the URL. - Not included: reading `DevToolsActivePort` for the caller, a pipe transport (option 3), or anything that launches a browser. **Questions** 1. Is this boundary acceptable? The toggle attaches to the user's real profile, while the docs currently recommend a dedicated one. Passive tools keep their existing data minimization, and Chrome adds its own consent dialog. 2. Should `revision` keep each transport's source value (my plan, documented), or should REA normalize it? This complements #2287, which documents option 1.
bsakaguchi 2026-10-31 00:29:30 +00:00
Author
Owner

@CassieFox23 commented on GitHub (Oct 18, 2026):

Checked on Linux: part of this is fixed on main by 62c8b3da1 (#2346). The commit's message says "Refs #2288", which is why GitHub left this issue open.

  • The commit's five test files pass on main 36d156ff0 on Node 22.23.1, 24.18.0 and 26.11.1 (31 tests).
  • On the commit's parent, 11 of 28 fail, ZodError (no ws:// support).

The tests cover option 2 only, a loopback ws:// URL. They skip option 3 (pipe transport) and a real Chrome run. So part of this may still be open.

Thanks, 0xRafie, for the clear report.

🤖 Generated with Claude Code (Sonnet 5.5, verified by Opus 5.5) 🦊

<!-- gh-comment-id:6254901949 --> @CassieFox23 commented on GitHub (Oct 18, 2026): Checked on Linux: part of this is fixed on main by 62c8b3da1 (#2346). The commit's message says "Refs #2288", which is why GitHub left this issue open. - The commit's five test files pass on main 36d156ff0 on Node 22.23.1, 24.18.0 and 26.11.1 (31 tests). - On the commit's parent, 11 of 28 fail, `ZodError` (no `ws://` support). The tests cover option 2 only, a loopback `ws://` URL. They skip option 3 (pipe transport) and a real Chrome run. So part of this may still be open. Thanks, 0xRafie, for the clear report. 🤖 Generated with Claude Code (Sonnet 5.5, verified by Opus 5.5) 🦊
Author
Owner

@morluto commented on GitHub (Oct 18, 2026):

Closing as completed by 62c8b3da1 on main.

The design decision is implemented as option 2 in #2346: passive tools accept a literal loopback browser WebSocket and share its discovery/session connection. This closes the no-HTTP-discovery design question. A private browser without any exposed CDP endpoint remains unsupported; pipe transport was not implemented.

This is source/history reconciliation using the existing validation record; no new local test or provider run is claimed.

<!-- gh-comment-id:6256019607 --> @morluto commented on GitHub (Oct 18, 2026): <!-- rea-backlog-reconciliation-2026-10-19-2288 --> Closing as completed by [62c8b3da1](https://github.com/morluto/rea/commit/62c8b3da1b00260a5ef97d088737dafa0a7ea542) on `main`. The design decision is implemented as option 2 in #2346: passive tools accept a literal loopback browser WebSocket and share its discovery/session connection. This closes the no-HTTP-discovery design question. A private browser without any exposed CDP endpoint remains unsupported; pipe transport was not implemented. This is source/history reconciliation using the existing validation record; no new local test or provider run is claimed.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
morluto/rea#756
No description provided.