Skip to content

[Design] Attaching to Chromium browsers that expose no HTTP discovery endpoint #2288

Closed

Description

@0xRafie

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. docs(browser): clarify attach coverage and Chromium setup #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

Activity

  1. all-black-493 commented on Oct 12, 2026

    @all-black-493

    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.

  2. added
    designArchitecture, provider, or contract decisions needed before implementation
    area: runtimeProcess capture and runtime observation
    triageAwaiting triage and prioritization
    P3: lowLow priority, nice to have
    and removed
    triageAwaiting triage and prioritization
    on Oct 12, 2026
  3. CassieFox23 commented on Oct 18, 2026

    @CassieFox23
    Contributor

    Checked on Linux: part of this is fixed on main by 62c8b3d (#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 36d156f 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) 🦊

  4. morluto commented on Oct 18, 2026

    @morluto
    Owner

    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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    P3: lowLow priority, nice to havearea: runtimeProcess capture and runtime observationdesignArchitecture, provider, or contract decisions needed before implementationenhancementNew feature or request

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions