The nes protocol consists of JSON messages sent between the client and server.
Each incoming request from the client to the server contains:
type- the message type:'ping'- heartbeat response.'hello'- connection initialization and authentication.'reauth'- authentication refresh.'request'- endpoint request.'sub'- subscribe to a path.'unsub'- unsubscribe from a path.'message'- send custom message.
id- a unique per-client request id (number or string).- additional type-specific fields.
Each outgoing request from the server to the client contains:
type- the message type:'ping'- heartbeat request.'hello'- connection initialization and authentication.'reauth'- authentication refresh.'request'- endpoint request.'sub'- subscribe to a path.'unsub'- unsubscribe from a path.'message'- send custom message.'update'- a custom message push from the server.'pub'- a subscription update.'revoke'- server forcedly removed the client from a subscription.
- additional type-specific fields.
If a message is too large to send as a single WebSocket update, it can be chunked into multiple
messages. After constructing the JSON message string, the string is sliced into chunked and each is
sent with the '+' prefix except for the last chunk sent with the '!' prefix.
When a message indicates an error, the message will include in addition to the message-specific fields:
statusCode- an HTTP equivalent status code (4xx, 5xx).headers- optional headers related to the request.payload- the error details which include:error- the HTTP equivalent error message.message- a description of the error.- additional error-specific fields.
For example:
{
type: 'hello',
id: 1,
statusCode: 401,
payload: {
error: 'Unauthorized',
message: 'Unknown username or incorrect password'
}
}Flow: server -> client -> server
For cases where it is not possible for the TCP connection to determine if the connection is still active, the server sends a heartbeat message to the client every configured interval, and then expects the client to respond within a configured timeout. The server sends:
type- set to'ping'.
For example:
{
type: 'ping'
}When the client receives the message, it sends back:
type- set to'ping'.id- a unique per-client request id (number or string).
For example:
{
type: 'ping',
id: 6
}Flow: client -> server -> client
Every client connection must first be initialized with a hello message. The client sends a message to the server
with the following:
type- set to'hello'.id- a unique per-client request id (number or string).version- set to'2'.auth- optional authentication credentials. Can be any value understood by the server.subs- an optional array of strings indicating the path subscriptions the client is interested in.
For example:
{
type: 'hello',
id: 1,
version: '2',
auth: {
headers: {
authorization: 'Basic am9objpzZWNyZXQ='
}
},
subs: ['/a', '/b']
}The server responds by sending a message back with the following:
type- set to'hello'.id- the sameidreceived from the client.heartbeat- the server heartbeat configuration which can be:false- no heartbeats will be sent.- an object with:
interval- the heartbeat interval in milliseconds.timeout- the time from sending a heartbeat to the client until a response is expected before a connection is considered closed by the server.
socket- the server generated socket identifier for the connection.
Note: the client should assume the connection is closed if it has not heard from the server in heartbeat.interval + heartbeat.timeout.
For example:
{
type: 'hello',
id: 1,
heartbeat: {
interval: 15000,
timeout: 5000
},
socket: 'abc-123'
}If the request failed (including subscription errors), the server includes the standard error fields.
For example:
{
type: 'hello',
id: 1,
statusCode: 401,
payload: {
error: 'Unauthorized',
message: 'Unknown username or incorrect password'
}
}If the request fails due to a subscription error, the server will include the failed subscription path in the response:
path- the requested path which failed to subscribe.
For example:
{
type: 'hello',
id: 1,
path: '/a',
statusCode: 403,
payload: {
error: 'Subscription not found'
}
}Flow: client -> server -> client
When the authentication credentials have an expiry, the client may want to update the authentication information for the connection:
type- set to'reauth'.id- a unique per-client request id (number or string).auth- authentication credentials. Can be any value understood by the server.
For example:
{
type: 'reauth',
id: 1,
auth: {
headers: {
authorization: 'Basic am9objpzZWNyZXQ='
}
}
}The server responds by sending a message back with the following:
type- set to'reauth'.id- the sameidreceived from the client.
For example:
{
type: 'reauth',
id: 1
}If the request failed, the server includes the standard error fields.
For example:
{
type: 'reauth',
id: 1,
statusCode: 401,
payload: {
error: 'Unauthorized',
message: 'Unknown username or incorrect password'
}
}Flow: client -> server -> client
Request a resource from the server where:
type- set to'request'.id- a unique per-client request id (number or string).method- the corresponding HTTP method (e.g.'GET').path- the requested resource (can be an HTTP path or resource name).headers- an optional object with the request headers (each header name is a key with a corresponding value).payload- an optional value to send with the request.
For example:
{
type: 'request',
id: 2,
method: 'POST',
path: '/item/5',
payload: {
id: 5,
status: 'done'
}
}The server response includes:
type- set to'request'.id- the sameidreceived from the client.statusCode- an HTTP equivalent status code.payload- the requested resource.headers- optional headers related to the request (e.g. `{ 'content-type': 'text/html; charset=utf-8' }').
For example:
{
type: 'request',
id: 2,
statusCode: 200,
payload: {
status: 'ok'
}
}If the request fails, the statusCode, headers, and payload fields will comply with the
standard error values.
Flow: client -> server [-> client]
Sends a custom message to the server where:
type- set to'message'.id- a unique per-client request id (number or string).message- any value (string, object, etc.).
For example:
{
type: 'message',
id: 3,
message: 'hi'
}The server response includes:
type- set to'message'.id- the sameidreceived from the client.message- any value (string, object, etc.).
For example:
{
type: 'message',
id: 3,
message: 'hello back'
}If the request fails, the response will include the standard error fields.
Flow: client -> server [-> client]
Sends a subscription request to the server:
type- set to'sub'.id- a unique per-client request id (number or string).path- the requested subscription path.
For example:
{
type: 'sub',
id: 4,
path: '/box/blue'
}The server response includes:
type- set to'sub'.id- the sameidreceived from the client.path- the requested path which failed to subscribe.- the standard error fields if failed.
For example:
{
type: 'sub',
id: 4,
path: '/box/blue',
statusCode: 403,
payload: {
error: 'Forbidden'
}
}Flow: client -> server -> client
Unsubscribe from a server subscription:
type- set to'unsub'.id- a unique per-client request id (number or string).path- the subscription path.
For example:
{
type: 'unsub',
id: 5,
path: '/box/blue'
}The server response includes:
type- set to'sub'.id- the sameidreceived from the client.- the standard error fields if failed.
For example:
{
type: 'unsub',
id: 5
}Flow: server -> client
A custom message sent from the server to a specific client or to all connected clients:
type- set to'update'.message- any value (string, object, etc.).
{
type: 'update',
message: {
some: 'message'
}
}Flow: server -> client
A message sent from the server to all subscribed clients:
type- set to'pub'.path- the subscription path.message- any value (string, object, etc.).
{
type: 'pub',
path: '/box/blue',
message: {
status: 'closed'
}
}Flow: server -> client
The server forcefully removed the client from a subscription:
type- set to'revoke'.path- the subscription path.message- any value (string, object, etc.). An optional last message sent to the client for the specified subscription.
For example:
{
type: 'revoke',
path: '/box/blue',
message: {
reason: 'channel permissions changed'
}
}