> Status: draft. Part of the Harmovela 0.2 transport-websocket profile.
Define how Harmovela runs over WebSocket, supporting bidirectional event streams over TCP with optional TLS.
ws://host:port/path
wss://host:port/path (TLS)
The reference runtime defaults its WebSocket path to /harmovela. Deployments may configure a different path.
The WebSocket handshake must request the subprotocol harmovela-0.2:
GET /harmovela HTTP/1.1
Upgrade: websocket
Sec-WebSocket-Protocol: harmovela-0.2
The server must respond with Sec-WebSocket-Protocol: harmovela-0.2 on success.
Future protocol versions use distinct subprotocol strings (e.g., harmovela-0.3). The server may advertise supported versions, and the client selects one.
Path and query string may carry initialization parameters (e.g., ws://host/harmovela?session_id=sess_01). This is transport-level metadata and not part of the protocol envelope.
| Aspect | Specification |
|---|---|
| Frame type | Text frames only (opcode 0x1) |
| Message format | One complete JSON Harmovela event per message |
| Encoding | UTF-8 |
| Multi-frame | Not used. Each event is a single text frame. |
| Control frames | Ping/Pong used for heartbeat (see below) |
| WebSocket State | Session State | Notes |
|---|---|---|
| Connecting | CREATED | |
| Open (after handshake) | OPENED | |
After session.ready | READY | |
| Close frame (1000) | CLOSED | Graceful shutdown |
| Close frame (1001–1015) | CLOSED | Shutdown with reason |
| Connection error | ERROR | Network failure |
| Protocol error (1002) | ERROR | Framing or message error |
WebSocket Ping/Pong frames serve as the transport-level heartbeat:
session.ready via capabilities.heartbeat_interval_ms.heartbeat_interval_ms should close the connection with code 1001 (going away) and transition the session to ERROR.Application-level session.heartbeat events are still sent over text frames and carry structured metadata. Transport-level Ping is a simpler, lower-overhead mechanism for keep-alive.
A disconnected client may reconnect and resume the event stream from a cursor:
1. Client opens a new WebSocket connection. 2. Client sends session.opened with a new or existing session_id. 3. Client sends subscription.requested with from_cursor set to the last received cursor value. 4. Server replays events from the cursor position.
Cursor format is transport-specific. For WebSocket, the cursor is the event id plus a sequence number:
evt_01JZ0000000000000000000000:42
Standard WebSocket close codes are mapped to Harmovela semantics:
| Code | Meaning | Harmovela Mapping |
|---|---|---|
| 1000 | Normal closure | session.closed with reason "normal" |
| 1001 | Going away | session.closed with reason "peer_disconnected" |
| 1002 | Protocol error | session.error with code protocol_error |
| 1008 | Policy violation | session.error with code unauthorized |
| 1011 | Internal error | session.error with code internal_error |
wss://) is strongly recommended for any network-exposed deployment.