rea/docs/browser-observation.md

12 KiB

Website observation with CDP

REA can attach to a user-owned Chrome-family browser through the Chrome DevTools Protocol (CDP) and produce bounded Evidence about an existing page. This is a passive reverse-engineering capability, not a general browser automation or remote-control surface.

Shipped surfaces

  • list_browser_targets / rea list-browser-targets discovers page targets whose current URL matches an approved exact origin.
  • inspect_web_page / rea inspect-web-page captures DOM structure, accessibility nodes, scripts, resources, safe response/DOM metadata, attach-window network and console metadata, WebSocket frame sizes, workers, quota, and optionally storage key names, redacted storage-content fingerprints, or script sources.
  • analyze_web_bundle / rea analyze-web-bundle parses approved script artifacts without execution and derives chunk edges, route and endpoint candidates, vendor fingerprints, static WebMCP declarations, and optionally approved source-map/original-source evidence.
  • observe_web_session / rea observe-web-session arms an observation window of the requested duration for an external user action and records ordered reload, SPA navigation, redirect, failure, lifecycle, and target-termination metadata.
  • discover_webmcp_tools / rea discover-webmcp-tools uses the experimental CDP WebMCP domain to return every in-scope registration and its complete structural input-schema summary inline. REA never exposes WebMCP.invokeTool; malformed protocol payloads fail explicitly instead of returning incomplete tool metadata.
  • compare_web_captures / rea compare-web-captures compares passive captures or exact-step-aligned browser scenarios. Scenario results commit reproducible literal normalization and expose alignment failures plus artifact-level action, screenshot, DOM, accessibility, URL, history, storage, and event differences. Missing or truncated evidence is never treated as equivalence.
  • capture_web_screenshot / rea capture-web-screenshot returns an explicitly approved, content-addressed visible-viewport PNG.
  • compare_web_screenshots / rea compare-web-screenshots performs bounded local PNG pixel comparison without OCR or external services.
  • Every surface has equivalent CLI and MCP contracts and returns Evidence provenance.
  • MCP tools return complete results inline and include the Evidence ID. Session evidence can be exported with export_evidence_bundle.

Target discovery returns the complete approved target array in one result.

Electron file:// pages use a separate provider, permission capability, and root model; see electron-observation.md. Existing static application Evidence and passive web/Electron captures can be combined later through JavaScript static/runtime reconciliation.

Start a browser

Start a separate browser profile with an explicit debugging port. The exact executable name varies by platform and installation:

google-chrome \
  --remote-debugging-port=9222 \
  --user-data-dir=/tmp/rea-browser-profile \
  http://127.0.0.1:3000

REA does not launch, own, or terminate this browser. Use a dedicated profile and stop it yourself when the investigation is complete. Do not expose the debugging port on a non-loopback interface.

Configure authority

Browser observation is fail-closed by default. Configure the administrator ceiling before starting REA:

export REA_BROWSER_OBSERVE_ENABLED=true
export REA_BROWSER_CDP_ENDPOINTS_JSON='["http://127.0.0.1:9222"]'
export REA_BROWSER_ALLOWED_ORIGINS_JSON='["http://127.0.0.1:3000"]'

REA_BROWSER_CDP_ENDPOINTS_JSON accepts at most 16 literal http://127.0.0.1:PORT or http://[::1]:PORT endpoints. localhost, private-LAN addresses, HTTPS endpoints, credentials, paths, queries, fragments, and implicit ports are rejected.

REA_BROWSER_ALLOWED_ORIGINS_JSON accepts at most 32 exact HTTP(S) origins. Paths, credentials, queries, fragments, and wildcards are rejected. An input must stay within both this administrator ceiling and any active project/session/once grant.

Configuration is reloadable through the existing SIGHUP permission-policy path. Each request is checked against the configured endpoint, origin, and permission grants.

Inspect the exact effective scope without contacting the browser:

rea policy explain browser_observe \
  --origins http://127.0.0.1:9222 \
  --origins http://127.0.0.1:3000 \
  --network loopback \
  --json

CLI workflow

Configured origins are used by default. --allowed-origins can request a narrower set that still fits the configured ceiling.

rea list-browser-targets http://127.0.0.1:9222 --json
rea inspect-web-page http://127.0.0.1:9222 TARGET_ID \
  --observation-ms 1000 \
  --json

Static bundle analysis and a user-driven observation window are separate operations:

rea analyze-web-bundle http://127.0.0.1:9222 TARGET_ID \
  --json
rea observe-web-session http://127.0.0.1:9222 TARGET_ID \
  --observation-ms 10000 --json
rea discover-webmcp-tools http://127.0.0.1:9222 TARGET_ID \
  --json

Accessibility names/descriptions, console primitive text, JSON body shapes, WebSocket shapes, script content, and storage key names are selected independently:

rea inspect-web-page http://127.0.0.1:9222 TARGET_ID \
  --include-accessibility-text \
  --include-console-text \
  --include-json-body-shapes \
  --include-websocket-shapes \
  --include-script-sources \
  --include-storage-keys --include-storage-fingerprints \
  --json

Accessibility text and script content may contain application secrets and become part of the returned Evidence. Credential-shaped console substrings are redacted. JSON and WebSocket captures retain complete paths, types, and counts, never values or examples. Storage values remain redacted even when key-name capture is requested. --include-storage-fingerprints requests SHA-256 identity/value fingerprints, which support comparison only when cookies, DOM storage, IndexedDB, and Cache Storage were all captured completely.

Screenshot capture returns the complete visible viewport as an inline image artifact:

rea capture-web-screenshot http://127.0.0.1:9222 TARGET_ID \
  --json

MCP input

{
  "cdp_endpoint": "http://127.0.0.1:9222",
  "allowed_origins": ["http://127.0.0.1:3000"],
  "target_id": "TARGET_ID_FROM_LIST_BROWSER_TARGETS"
}

The omitted observation window and capture options use conservative defaults. Set an option only when the investigation needs that additional content. Collection counts are not capped. Per-value validation, the selected observation window, and CDP protocol limits still apply; any incomplete or truncated values are reported in the result.

Call list_browser_targets first because target IDs are browser-instance-specific. REA rechecks the selected target's current type and origin immediately before attaching.

REA establishes an authorized main-frame boundary before enabling Runtime, Debugger, or Network observation, rechecks it before document capture, and validates it again afterward. If the main frame navigates during final capture, REA discards the mixed result and returns target_changed.

Data minimization

REA removes sensitive values before normalized event data is retained:

  • URLs retain a bounded path and at most 256 bounded query parameter names, replace every retained query value with [REDACTED], and remove credentials and fragments.
  • DOM snapshots retain node types, node names, value lengths, and attribute names, but not text or attribute values.
  • Accessibility structure and roles are retained by default, while names and descriptions require include_accessibility_text: true.
  • Network observations retain method, status, MIME type, size, type, initiator stack location, and redacted URLs. Headers are discarded after an allowlisted projection of length/encoding, structured CSP/Link/policy fields, and untrusted agent hints. Cookies and authorization headers are never retained.
  • Request/response bodies are not requested or parsed by default. When selected, only allowed-origin JSON media types are read, converted immediately to complete value-free property paths/types, and discarded. Network.getResponseBody is never sent unless JSON body shape capture is selected.
  • Console observations with a stack source retain call type, argument types, timestamp, and redacted source location. When console text capture is selected, already-delivered primitive values are retained after credential redaction; objects, getters, and remote properties are never expanded.
  • WebSocket observations retain direction, opcode, and payload byte length. When shape capture is selected, text frames are classified as text or complete value-free JSON shape; binary bytes, hashes, prefixes, and raw frames are never retained.
  • Storage observations always redact values. Key names, IndexedDB names, and cache names require include_storage_keys; stable content fingerprints require the additional include_storage_fingerprints selection. Cache bodies above 64 KiB and partial IndexedDB remote objects make fingerprint coverage incomplete, so identical observations remain unknown.
  • Script metadata is included only when CDP supplies a URL on an allowed origin. Stable keys exclude transient CDP script IDs, and exact transient raw URLs are used only during script/resource reconciliation. URL-less scripts are excluded because their origin cannot be established. When CDP supplies an execution-context association, the accepted script retains its authorized frame ID for later attribution. Source content is omitted unless explicitly requested and is returned inline as a self-verifying artifact with its SHA-256 digest.

Cross-origin frames, resources, scripts, events, and workers are excluded unless their exact origins are also approved. Excluded target details are counted without being exposed. Retained workers include validated opener-target and parent-frame IDs when CDP provides them; these relationships do not expand the exact-origin boundary or prove static module ownership.

Completeness and limits

The default inspection window is 500 ms; callers choose its duration. The capture returns all in-scope frames, DOM and accessibility nodes, scripts, resources, workers, storage names, and observed network, console, and WebSocket events. Full selected text, script sources, and JSON shapes are returned inline. The result distinguishes complete_within_window, policy_filtered, attach_limited, and truncated coverage. It reports excluded items and any values omitted or truncated by validation; it does not drop records to meet an internal collection quota. Disallowed-origin entries are filtered before retention.

CDP discovery, WebSocket connection, and each command have a 5-second timeout. Version discovery is capped at 64 KiB, target discovery at 2 MiB, each WebSocket message at 16 MiB, and correlated pending commands at 128. Target and navigation-event inventories have no separate count cap. These transport bounds protect protocol framing and request correlation; they do not silently trim returned findings. Malformed unsolicited protocol events poison the connection so later commands fail closed.

Network and console coverage starts only after REA attaches and enables the relevant CDP domains. prior_activity_available is always false; absence from these arrays is not evidence that an event never occurred. Source maps are not fetched by inspection. Bundle analysis can fetch them only after separate approval, with exact-origin redirect checks and no credentials, cookies, or referrer. Every requested map, mapping, original source, AST node, and finding from parsed content is returned inline; failed maps remain explicitly unavailable and make bundle completeness partial.

Non-goals and threat model

The browser provider deliberately does not expose generic CDP commands, Runtime.evaluate, WebMCP invocation, navigation, input, downloads, page closure, or browser closure. Screenshot capture is the sole pixel surface and requires separate literal approval. REA disables the domains it enabled, detaches the target session, and closes only its own WebSocket.

This feature is not a browser sandbox or network containment mechanism. The attached page and browser continue running with their existing privileges and may make external requests independently of REA. CDP gives deep access to the selected browser profile, so use a dedicated profile, approve only origins you intend to inspect, and treat other same-user processes as outside this boundary.