Wire protocol 1.x
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.
Protocol 1.x, for existing apps. Protocol 2.0 is live, and every new app from the dashboard
uses it: start with the quickstart. This page is for apps that still run on 1.x, on
ws.pikoman.se and api.pikoman.se. Moving to 2.0.
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.