WebSocket and WebUI

Corresponding source

SubjectSource
WebSocket Channel and protocolnanobot/channels/websocket.py
WebUI session coordinationnanobot/session/webui_turns.py
WebUI backend routes and runtime datananobot/webui/
Frontend client and message renderingwebui/src/lib/nanobot-client.ts, webui/src/hooks/useNanobotStream.ts, webui/src/components/
WebSocket/WebUI teststests/channels/test_websocket_*.py, tests/webui/

Learning objectives

After this lesson, you should be able to:

  • explain why WebUI depends on the WebSocket Channel and Gateway;
  • distinguish WebSocket frames from runtime events;
  • understand multi-chat multiplexing, stream segments, and authentication boundaries;
  • locate the Python-side entry point for UI state coordination.

Prerequisites

  • Understand ChannelManager.
  • Understand RuntimeEventBus.
  • Know that WebSocket is a bidirectional, long-lived connection.

1. WebUI is not the Gateway health page

Default ports:

SurfaceDefault port
WebSocket Channel / WebUI8765
Gateway health18790
OpenAI-compatible API8900

WebUI static assets are served by HTTP services associated with the WebSocket Channel. Port 18790 is primarily Gateway health, not the browser workspace.

2. Structure

flowchart TD
    UI["React/TypeScript WebUI"] --> HTTP["Static assets and WebUI HTTP API"]
    UI <--> WS["WebSocket Channel"]
    WS <--> BUS["MessageBus"]
    BUS <--> AL["AgentLoop"]
    AL --> RE["RuntimeEventBus"]
    RE --> WTC["WebuiTurnCoordinator"]
    WTC --> BUS
    HTTP --> GS["Gateway services"]
    GS --> SM["Session / Cron / Skills / Settings"]

WebUI uses:

  • WebSocket conversation and real-time events;
  • HTTP routes for settings, media, Skills, Apps, Cron, and related management;
  • Session and workspace metadata.

3. WebSocket connection

Connection URL:

ws://<host>:<port><path>?client_id=<id>&token=<token>

Authentication may use:

  • a static token;
  • a short-lived issued token;
  • tokenIssueSecret;
  • websocketRequiresToken.

allowFrom still controls the client ID.

When binding publicly to 0.0.0.0, Gateway requires a token or token issue secret to avoid unauthenticated exposure.

4. Multi-chat multiplexing

One WebSocket connection can subscribe to multiple chat IDs. Client envelopes include:

  • new_chat
  • attach
  • message

The server confirms with:

  • attached

Consequences:

  • connection ID is not chat ID;
  • one browser connection can carry several Sessions;
  • the client can attach again after a page reload;
  • stream buffers must be separated by both chat ID and stream ID.

5. Primary server events

EventPurpose
readyconnection established
messagecomplete response
deltatext increment
stream_endcurrent text segment ended
reasoning_deltareasoning increment
reasoning_endreasoning segment ended
runtime_model_updatedruntime model changed
coordination events such as goal_statesynchronize UI state
errorsoft protocol error; connection may remain open

Verify fields in the WebSocket documentation and nanobot/channels/websocket.py.

6. Stream ID

One chat can contain several stream segments:

Model emits text
  → calls a Tool
  → continues emitting text

The UI therefore cannot concatenate every delta forever by chat ID alone. _stream_id distinguishes segments, and stream_end closes the current segment.

7. Runtime event and WebSocket frame

RuntimeEventBus publishes general Python events:

  • Turn started;
  • run status changed;
  • Turn completed;
  • Goal changed;
  • runtime model changed.

WebuiTurnCoordinator or the WebSocket adapter converts them into UI frames.

Runtime event: core internal semantics
WebSocket frame: client wire protocol
React state: final presentation state

Keep these three layers separate.

8. Python-side WebUI services

nanobot/webui/ contains:

  • settings routes/API;
  • Skills API;
  • MCP presets;
  • CLI Apps;
  • media;
  • file preview;
  • Session list/index;
  • automations;
  • Gateway token handling;
  • workspaces;
  • token usage;
  • thread disk/display state.

Not every operation passes through AgentLoop. Changing settings or listing Skills is a management action, not a user Agent Turn.

9. React frontend

Its source is under webui/. Study it in depth only when changing browser presentation:

  • Vite;
  • React;
  • TypeScript;
  • WebSocket client;
  • state and components.

Debug in this order:

  1. Did the core Turn complete?
  2. Was the runtime event published?
  3. Was the WebSocket frame delivered?
  4. Did the browser receive it?
  5. Did React update correctly?

Do not begin with CSS or a component when the unknown is backend state.

10. WebUI capability is not security authorization

WebUI can select a workspace and local access mode, but it cannot bypass:

  • Gateway configuration;
  • Tool workspace restrictions;
  • Shell sandbox;
  • operating-system permissions;
  • Provider permissions;
  • SSRF controls;
  • token/allowFrom.

A UI control is a capability-selection surface, not a new security root.

Source and tests

  • nanobot/channels/websocket.py
  • nanobot/session/webui_turns.py
  • nanobot/webui/
  • webui/
  • tests/channels/test_websocket_integration.py
  • tests/channels/test_websocket_http_routes.py
  • tests/channels/test_websocket_envelope_media.py
  • tests/webui/

Self-check

  1. Why is WebUI on 8765 while health is on 18790?
  2. How do connection ID, client ID, and chat ID differ?
  3. Why can a runtime event not be sent directly as WebSocket JSON?
  4. Why can one chat need several stream IDs?
  5. Why can WebUI access mode not bypass Tool security boundaries?

Next step

Read Provider resolution and fallback.