Protocol

Protocol

Symple v4 wire protocol specification

Symple Protocol v4

Real-time messaging and presence over WebSocket.

Transport

  • WebSocket (RFC 6455) over TCP
  • TLS recommended for production (wss://)
  • Keepalive: native WebSocket ping/pong frames (no application-level heartbeat)
  • Encoding: all messages are UTF-8 JSON text frames
  • No binary payloads

Connection lifecycle

Client                              Server
  |                                    |
  |--- WebSocket connect ------------->|
  |                                    |
  |--- {"type":"auth", ...} --------->|  Client sends auth
  |                                    |  Server validates credentials
  |<-- {"type":"welcome", ...} -------|  Success: peer assigned, rooms joined
  |  or                                |
  |<-- {"type":"error", ...} ---------|  Failure: connection closed
  |                                    |
  |<-- presence broadcasts ----------->|  Peer online/offline notifications
  |<-- messages ---------------------->|  Routed messages between peers
  |                                    |
  |--- {"type":"close"} ------------->|  Graceful disconnect
  |--- WebSocket close --------------->|

Message format

Every message is a JSON object with a type field:

{
  "type": "string",
  ...
}

Message types

auth (client → server)

First message after WebSocket connect. Server closes the connection if not received within 10 seconds.

{
  "type": "auth",
  "user": "alice",
  "name": "Alice",
  "token": "optional-auth-token",
  "rooms": ["team-a", "design"],
  "data": {}
}
FieldRequiredDescription
typeyes"auth"
useryesUser identifier (used for addressing and room membership)
namenoDisplay name (defaults to user)
tokennoAuth token (required if server has authentication enabled)
roomsnoRooms to auto-join (also set from Redis session or auth hook)
datanoArbitrary peer data (capabilities, status, etc.)

welcome (server → client)

Sent after successful authentication.

{
  "type": "welcome",
  "protocol": "symple/4",
  "peer": {
    "id": "a1b2c3d4",
    "user": "alice",
    "name": "Alice",
    "online": true,
    "host": "192.168.1.100"
  },
  "status": 200,
  "rooms": ["alice", "team-a", "design"]
}
FieldDescription
protocolProtocol version ("symple/4")
peerAuthenticated peer object
peer.idServer-assigned session ID (unique per connection)
statusHTTP-style status code (200 = success)
roomsRooms the peer was auto-joined to

error (server → client)

Sent on auth failure or protocol error. Server closes the connection after sending.

{
  "type": "error",
  "status": 401,
  "message": "Authentication failed: invalid token"
}

message (bidirectional, routed)

The core message type. Server routes based on the to field.

{
  "type": "message",
  "id": "msg_abc123",
  "from": "alice|a1b2c3d4",
  "to": "bob|x9y8z7w6",
  "subtype": "call:offer",
  "data": {
    "sdp": "v=0\r\n..."
  }
}
FieldRequiredDescription
typeyes"message"
idyesUnique message ID (client-generated)
fromyesSender address (`user
tonoRecipient. See routing rules below.
subtypenoApplication-defined (e.g. "call:offer", "chat")
datanoPayload (any JSON value)

Routing rules:

to valueBehaviour
"user|id"Direct to specific peer session
"user"Broadcast to user's room
omittedBroadcast to all sender's rooms (excluding sender)
["room1", "room2"]Broadcast to specific rooms

Direct messages (to: "user|id") require sender and recipient to share at least one room. Messages to peers without a shared room are silently dropped.

presence (bidirectional, routed)

Peer status updates. Server broadcasts to all rooms the peer has joined.

{
  "type": "presence",
  "from": "alice|a1b2c3d4",
  "data": {
    "id": "a1b2c3d4",
    "user": "alice",
    "name": "Alice",
    "online": true
  },
  "probe": false
}
FieldRequiredDescription
typeyes"presence"
fromyesSender address
tonoSpecific recipient (for directed presence)
datayesPeer object with at least id, user, online
probenoIf true, requests recipient to send their presence back

Automatic presence:

  • Server broadcasts online: true when a peer authenticates
  • Server broadcasts online: false when a peer disconnects
  • Server strips token from presence broadcasts

command (bidirectional, routed)

Structured commands with node/action addressing.

{
  "type": "command",
  "from": "alice|a1b2c3d4",
  "to": "bob|x9y8z7w6",
  "node": "media:video",
  "action": "start",
  "data": {}
}

event (bidirectional, routed)

Named events with arbitrary data.

{
  "type": "event",
  "from": "alice|a1b2c3d4",
  "to": "bob|x9y8z7w6",
  "name": "typing",
  "data": {}
}

join (client → server)

Join a room (requires dynamicRooms: true).

{"type": "join", "room": "public"}

Server response: {"type": "join:ok", "room": "public"}

leave (client → server)

Leave a room.

{"type": "leave", "room": "public"}

Server response: {"type": "leave:ok", "room": "public"}

close (client → server)

Graceful disconnect.

{"type": "close"}

Addressing

Peers are addressed as user|id:

  • user -- identifier from auth
  • id -- server-assigned session ID from welcome
  • user|id -- fully qualified (routes to specific session)
  • user alone -- routes to user's room

Rooms

Rooms are the permission boundary for messaging and presence.

  • Every peer auto-joins a room named after their user field
  • Additional rooms from auth hook, Redis session, or auth message
  • With dynamicRooms, peers can join/leave at runtime
  • Direct messages require a shared room
  • Room names are arbitrary strings

Status codes

CodeMeaning
200Success
400Bad request
401Unauthorized
403Forbidden (no shared room)
404Peer not found
408Auth timeout
500Internal server error

v3 → v4 changes

Featurev3 (Socket.IO)v4 (WebSocket)
TransportSocket.IO (Engine.IO + polling)Native WebSocket
HandshakeSocket.IO connect + auth middlewareauth message as first frame
HeartbeatEngine.IO ping/pong (app bytes)WebSocket ping/pong (protocol frames)
Eventssocket.emit('message', data)ws.send(JSON.stringify(data))
RoomsSocket.IO roomsServer-managed sets (same semantics)

The message format (type, from, to, data) is unchanged from v3. Client application code (CallManager, WebRTCPlayer, roster) is unaffected.