Wire protocol
Everything on the socket is a short text frame: a number, a colon, then the details. You only need this page to write a client for another language or to read the wire log in the demos.
Connecting
Open a WebSocket to wss://ws.pikoman.se/{appId}. The first frame you receive carries your
connection id:
042
Opcodes
| Code | Meaning | Client sends | Server sends |
|---|---|---|---|
| 0 | Connection | Never | 0:{connectionId} |
| 1 | Server message | Never | 1:{channel}:{event}:{payload} |
| 2 | Subscribe |
2:{channel}2:{channel}:{signature}:{permissions}:{data} for presence
|
2:{channel} as confirmation2:{channel}:{count}:{connectionId}:{data} when a member joins
|
| 3 | Join refused | Never | 3:{channel} when a presence signature is wrong |
| 4 | Unsubscribe | 4:{channel} |
4:{channel} as confirmation4:{channel}:{count}:{connectionId} when a member leaves
|
| 8 | Client message | 8:{channel}:{toAll}:{event}:{payload} |
8:{channel}:{senderId}:{event}:{payload} |
| 9 | Rate limited | Never | 9:RATE_LIMIT |
| 16 | Member list | Never | 16:{channel}:{members} after a presence join |
| 64 | Ping | 64:{timestamp} |
The same frame, echoed |
Only the first colons separate fields. The last field runs to the end of the frame, so payloads may contain colons freely.
A presence session, frame by frame
Connection 42 joins a room that already has one member, says hello, and receives a message published over HTTP.
| Direction | Frame | What happened |
|---|---|---|
| Received | 0:42 |
Connected, and this is our id. |
| Sent | 2:presence-room:9f2c…e1:3:{"name":"Ada"} |
Join, with the string our backend signed. |
| Received | 16:presence-room:{"17":"{\"name\":\"Linus\"}","42":"{\"name\":\"Ada\"}"} |
Everyone in the room, including us. Each value is that member's data as a JSON string. |
| Sent | 8:presence-room:1:message:{"text":"hello"} |
A client message. toAll is 1, so we get it back as well. |
| Received | 8:presence-room:42:message:{"text":"hello"} |
The relayed message, now carrying the sender's id. |
| Received | 1:presence-room:bot:{"text":"Welcome, Ada"} |
A server message, published with POST /presence-room/bot. |
| Received | 4:presence-room:1:17 |
Linus left. One member remains. |
Keeping the connection alive
The server echoes any frame it does not otherwise understand, which is how ping works. The JavaScript client
sends 64:{timestamp} after 6.5 seconds without traffic and reconnects if nothing arrives for 18.5
seconds. A client you write yourself should do something similar.
Limits
- Client messages need a presence channel and a join signed with write permission.
- One client message per connection every 20 ms. Extra ones are dropped and answered with
9:RATE_LIMIT. - Server messages cannot be sent over the socket. They come from the HTTP API only.