Skip to content

Relay HTTP endpoints

The Relay exposes a Chrome-compatible HTTP discovery surface alongside its WebSocket endpoints. The Node adapter (serveRelay, in src/relay/node.ts) runs two HTTP servers: a browser/CDP server for Client discovery and CDP WebSocket traffic, and a Host server for the Host uplink WebSocket plus optional fallback HTTP. The JSON payloads themselves come from RelayCore (jsonVersion(), jsonList(), status()), so any runtime adapter built on RelayCore serves the same shapes.

The four discovery routes below live on the browser/CDP server. They respond with Content-Type: application/json; charset=utf-8 and HTTP status 200; any other browser/CDP HTTP path returns 404. The Host server does not serve these routes. Set ICDP_DEBUG=1 to log every HTTP request and WebSocket upgrade.

GET routes

PathHandlerBody
/json/versionRelayCore.jsonVersion()Browser version descriptor
/jsonRelayCore.jsonList()Array of target descriptors
/json/listRelayCore.jsonList()Same array as /json
/icdp/statusRelayCore.status()Relay status snapshot

Any other browser/CDP HTTP path is answered with HTTP 404 and the body not found. The optional fallback handler only runs on the Host server.

GET /json/version

The browser version descriptor a Client reads to discover the WebSocket endpoint.

json
{
  "Browser": "icdp/0.1",
  "Protocol-Version": "1.3",
  "User-Agent": "icdp/0.1",
  "V8-Version": "synthetic",
  "WebKit-Version": "synthetic",
  "webSocketDebuggerUrl": "ws://127.0.0.1:9229/devtools/browser"
}
FieldTypeValue
BrowserstringThe product string (default icdp/0.1).
Protocol-Versionstring"1.3", fixed.
User-AgentstringThe product string.
V8-Versionstring"synthetic", fixed.
WebKit-Versionstring"synthetic", fixed.
webSocketDebuggerUrlstringAbsolute URL of the browser WebSocket endpoint (browserWsUrl).

GET /json and GET /json/list

Both paths return the same array, one entry per Target the Relay currently knows about.

json
[
  {
    "description": "icdp iframe target",
    "devtoolsFrontendUrl": "",
    "id": "playground",
    "title": "Playground",
    "type": "page",
    "url": "http://127.0.0.1:3001/playground",
    "webSocketDebuggerUrl": "ws://127.0.0.1:9229/devtools/browser"
  }
]
FieldTypeValue
descriptionstring"icdp iframe target", fixed.
devtoolsFrontendUrlstring"", fixed.
idstringThe targetId.
titlestringThe Target's last-known title.
typestring"page", fixed.
urlstringThe Target's last-known URL.
webSocketDebuggerUrlstringThe single browser endpoint (browserWsUrl) — the same value on every entry.

Every entry points at one URL

webSocketDebuggerUrl is the browser endpoint, not a per-target URL. icdp is flat-session only: there are no per-target WebSocket URLs. A Client connects once to the browser endpoint and attaches to a Target with Target.attachToTarget, then routes commands by sessionId. See the flat-session protocol.

GET /icdp/status

A snapshot of Relay state, for health checks and the playground status page.

json
{
  "hostConnected": true,
  "targets": [
    { "targetId": "playground", "title": "Playground", "url": "http://127.0.0.1:3001/playground" }
  ],
  "clients": 2
}
FieldTypeValue
hostConnectedbooleantrue when a Host uplink is attached.
targetsTargetSummary[]Each entry is { targetId, title, url }. See protocol types.
clientsnumberCount of connected Clients.

WebSocket upgrade paths

The Relay accepts WebSocket upgrades on exactly two paths, but they are on different servers. Both default values are configurable through ServeRelayOptions.

ServerPath optionDefaultRoleAdapter call
browser/CDPbrowserPath/devtools/browserClient connection (standard CDP)clientConnected / clientMessage / clientDisconnected
HosthostPath/icdp/hostHost uplink (bridge protocol)hostConnected / hostMessage / hostDisconnected

An upgrade on any other path is rejected: the socket is destroyed without an HTTP response. Only one Host is served at a time; a new Host connection drops the previous one (new-wins). The browser path appears verbatim as the webSocketDebuggerUrl in /json/version and /json.

The full URLs are read back from the RelayServer returned by serveRelay:

ts
import { serveRelay } from "@olimsaidov/icdp/relay/node";

const relay = await serveRelay({ hostPort: 3000, browserPort: 9229 });
relay.hostWsUrl;    // ws://127.0.0.1:3000/icdp/host          (Host uplink)
relay.browserWsUrl; // ws://127.0.0.1:9229/devtools/browser  (Clients)

See the Relay reference for the RelayCore adapter API and the ServeRelayOptions fields that set ports, hostnames, paths, advertised URLs, product, and fallback.

Released under the MIT License.