You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
When cortex-scout runs on a headless server (no DISPLAY, no X server), the HITL tools (hitl_web_fetch, scout_agent_profile_auth) cannot open a visible browser window. They exist in the binary (full --all-features build) but fail at runtime because there is no display to present the interactive window to a human.
This issue documents a working workaround: a small CDP relay that exposes the existing headless Chromium session (the one cortex-scout already manages via chromiumoxide) as a web viewer, giving a human operator remote interactive control (click, type, scroll, navigate) from any browser — effectively a portable HITL channel for headless deployments.
Environment
cortex-scout v3.3.7 built with cargo build --release --all-features on Debian 13 (trixie), x86_64
Headless VM: no DISPLAY, no X11/Wayland session
Chromium 150 system package
Observed behavior of hitl_web_fetch on this host: fails with user cancelled (the visible window cannot be created; no human can interact with it)
Why noVNC-style VNC is the wrong path
A classic VNC setup (Xvfb + x11vnc + websockify + noVNC) would require launching a second Chromium in non-headless mode. That means:
a different browser instance than the one cortex-scout is actually using → the CAPTCHA/auth wall being solved is not the one the agent is stuck on;
profile lock conflicts if the same --user-data-dir is reused;
cookies/session state divergence.
The interesting fact is that cortex-scout already runs a headless Chromium with a live CDP endpoint (chromiumoxide launches it with --remote-debugging-port=0, which binds a random localhost port). That endpoint is enough to build a faithful remote HITL channel with zero new browser instances.
The workaround: CDP relay → web viewer
Architecture:
[headless Chromium (cortex-scout)]
│ CDP (ws://127.0.0.1:<random port>, loopback only)
▼
[relay: Node.js, ws@8]
- discovers the CDP port via `ss -tlnp` (the chromium argv still says
`--remote-debugging-port=0`, so /proc parsing does NOT give the real port)
- connects to the page target
- Page.startScreencast (JPEG frames) ──► viewers
- Input.dispatchMouseEvent / Input.dispatchKeyEvent / Page.navigate ◄── viewers
│ HTTP :9092 + ?token= (auth)
▼
[browser of the human operator: static page, <canvas> + WebSocket]
Validation performed on the real system:
CDP discovery: ss -tlnp reliably finds the chromium listener (argv keeps --remote-debugging-port=0);
Input.dispatchMouseEvent (mouseMoved/mousePressed/mouseReleased) and Input.dispatchKeyEvent are accepted with no error;
Page.navigate works from the viewer;
the session is the same browser cortex-scout uses: cookies/profile untouched, same process PID observed from both the MCP tool and the relay;
scout_browser_automate and visual_scout keep working normally after a reconnect.
This turns the existing headless session into a remote "visible browser" for the duration of a wall, without touching cortex-scout's code.
Remaining gaps / not yet solved
Port discovery across restarts: the CDP port changes on every cortex-scout restart; the relay currently re-discovers it with ss each time a viewer connects. A stable channel (e.g. a documented fixed port or a handshake file) would be cleaner.
Multiple concurrent clients: Chrome tolerates several CDP clients, but concurrent viewer sessions were not stress-tested against an active scout_browser_automate run.
Auth/TLS: the PoC uses a static query-string token; production use should use TLS + stronger auth, and never expose the raw CDP port (full browser control) directly.
Walls that need a real human click with device-level trust (e.g. strict reCAPTCHA v3 scoring) may still flag CDP-dispatched input; this is inherent to any CDP-based approach.
Possible project-level improvements (nice to have)
Document the headless + HITL limitation and this relay pattern in the README.
Consider a CORTEX_SCOUT_CDP_PORT env var to pin --remote-debugging-port (instead of 0) for stable external attach.
Consider a built-in "remote HITL" transport (e.g. serve a small viewer next to the HTTP server when CORTEX_SCOUT_REMOTE_HITL=1) so no external relay is needed.
Links
Prior context: issue dependancies for HITL #4 "dependancies for HITL" (build deps for the full build on Debian) — closed; this issue is about the headless runtime limitation, not the build.
Summary
When cortex-scout runs on a headless server (no DISPLAY, no X server), the HITL tools (
hitl_web_fetch,scout_agent_profile_auth) cannot open a visible browser window. They exist in the binary (full--all-featuresbuild) but fail at runtime because there is no display to present the interactive window to a human.This issue documents a working workaround: a small CDP relay that exposes the existing headless Chromium session (the one cortex-scout already manages via
chromiumoxide) as a web viewer, giving a human operator remote interactive control (click, type, scroll, navigate) from any browser — effectively a portable HITL channel for headless deployments.Environment
cargo build --release --all-featureson Debian 13 (trixie), x86_64hitl_web_fetchon this host: fails withuser cancelled(the visible window cannot be created; no human can interact with it)Why noVNC-style VNC is the wrong path
A classic VNC setup (Xvfb + x11vnc + websockify + noVNC) would require launching a second Chromium in non-headless mode. That means:
--user-data-diris reused;The interesting fact is that cortex-scout already runs a headless Chromium with a live CDP endpoint (
chromiumoxidelaunches it with--remote-debugging-port=0, which binds a random localhost port). That endpoint is enough to build a faithful remote HITL channel with zero new browser instances.The workaround: CDP relay → web viewer
Architecture:
Validation performed on the real system:
ss -tlnpreliably finds the chromium listener (argv keeps--remote-debugging-port=0);Page.startScreencaststreams JPEG frames continuously (confirmed 3+ frames on visual change);Input.dispatchMouseEvent(mouseMoved/mousePressed/mouseReleased) andInput.dispatchKeyEventare accepted with no error;Page.navigateworks from the viewer;scout_browser_automateandvisual_scoutkeep working normally after a reconnect.This turns the existing headless session into a remote "visible browser" for the duration of a wall, without touching cortex-scout's code.
Remaining gaps / not yet solved
sseach time a viewer connects. A stable channel (e.g. a documented fixed port or a handshake file) would be cleaner.scout_browser_automaterun.Possible project-level improvements (nice to have)
CORTEX_SCOUT_CDP_PORTenv var to pin--remote-debugging-port(instead of0) for stable external attach.CORTEX_SCOUT_REMOTE_HITL=1) so no external relay is needed.Links