SNP 3.1 Developer Guide - fullphat/snarl_network_protocol GitHub Wiki
- Introduction
- Overview
- Message Structure
i. Requests
ii. Responses
iii. Signals - Forwarding
- Subscriptions
- Security
i. Authorisation
ii. Encryption
Introduction
SNP 3.1 adds a number of improvements to SNP 3.0 while maintaining several of the features the earlier version of the protocol introduced. Among the changes introduced in SNP 3.1 are:
- Standardised processing: every SNP 3.1 request must be accompanied by a resulting SNP 3.1 response. Furthermore, spurious responses (e.g. forwarded notifications and callbacks) should be sent to a different port that has been created specifically to receive these out-of-cycle messages;
- Structured messages: SNP 3.1 defines a number of standard request messages which may be sent by a client;
- New message type: SNP 3.1 defines a signal message, which is sent from a server to a client, but does not need a reply (previously this was known as an out-of-cycle message);
- Clearer message format: SNP 3.1 requests, responses and signals now follow the same template approach;
- Granular security controls: servers listening for incoming SNP 3.1 messages should provide the user with the ability to block forwarded notifications, and to require authorisation for such notifications;
- Flexibility: callbacks may be directed to a separate port created specifically for receiving out-of-cycle messages, or POSTed to a URL;
- More descriptive errors: in addition to returning the code and name of the error that occurred, SNP 3.1 also defines a further parameter that allows the server to return more context around why the error occurred.
Overview
Communication Cycle
A SNP 3.1 conversation is held between a client and a server with the client initiating the request, and the server responding to the request appropriately. The process is as follows:
- Client opens a connection to the server
- Client issues a request
- Server takes action and issues an appropriate response
- Client either closes the connection or repeats step 2
The client and server may be on the same physical machine, or they may be on different machines. If a client wishes to send a request and then close, it must wait until it has received the response from the server, even if it does not plan to do anything with it.
Callbacks can be received on the same port used by the client to issue the requests, however this is not recommended as it requires more complex logic within the client to distinguish between a response to a previously issued request, and an out-of-cycle callback message. The recommendation is for the client application to create a separate listening socket and pass the port number of that socket when creating notifications. This then allows the client to run a tight send/receive message loop while still able to receive callbacks and other asynchronous events.
Other Metrics
Default Port
SNP 3.1 does not define a standard port, however convention is to use TCP port 31000.
Text Encoding
Content values should be encoded as UTF-8.
Messages
A SNP 3.1 message comprises of a header, zero or more lines of content, and a terminator. Like SNP 3.0, a SNP 3.1 message spans multiple lines and can be a variable number of lines. The terminator is always END
. Each line, including the terminator, must end with a CRLF
.
To provide greater operating system compatibility, recent versions of SNP 3.1 also support a single
CR
(0x0D
or\r
), a singleLF
(0x0A
or\n
), or even aLFCR
pair in the request. Note however that responses always useCRLF
as the line end marker.
Three message types are defined:
- Request: issued from a client to a server asking the server to do something;
- Response: issued from a server to a client in reply to a request. The response will indicate whether the previous request succeeded or not;
- Signal: issued asynchronously from a server to a port specified by the client. Signals indicate events which may occur out of the request/response sequence - for example, the user triggering a callback by acknowledging a notification.
All SNP 3.1 messages are very similar in format, however requests must include at least one line of content; responses and signals may not include content. Also, requests must be responded to; responses and signals are not responded to.
Request Structure
Header
The header describes the nature of the request:
{id/version} {action} [{authorisation} [{encryption}]]
Item | Description |
---|---|
id/version |
Indicates the version of the protocol to be used. Always SNP/3.1 . |
action |
Indicates the request type. There are currently five types of request: FORWARD , NOTIFY , REGISTER , SUBSCRIBE , UNSUBSCRIBE . |
authorisation |
The algorithm, key hash and salt used if authorisation is required. |
encryption |
The encryption algorithm and initialisation value used to secure the message content if it's encrypted. |
Content
Each line of content is formatted in a similar way to MIME and HTTP headers, as a key/value pair separated with a colon and a space character:
title: Hello, world!
text: This is some text...
The Message Reference explains what content is required for each request type.
Example Request
SNP/3.1 NOTIFY
title: Testing...
text: Hello, world!
icon: stock:system-info
END
Response Structure
Header
The response header is as follows:
{id/version} {responseType}
Item | Description |
---|---|
id/version |
Returns the highest version of the protocol supported. Always SNP/3.1 . |
responseType |
Indicates the response type. There are two types of response: FAILED and SUCCESS . |
Content
SUCCESS
responses typically do not include any additional content, however SNP 3.1 servers are free to include content if they wish; FAILED
responses include the following content by default:
Item | Description |
---|---|
error—number |
The Snarl status code as an integer. |
error—name |
The Snarl status code as text. |
reason |
Human-readable text which may be included to provide more specific information about what went wrong. |
Example Responses
SNP/3.1 SUCCESS
END
SNP/3.1 FAILED
error-number: 135
error-name: HashValueMismatch
reason: The request hash type is invalid
END
Signal Structure
Header
The response header is as follows:
{id/version} {signalType}
Item | Description |
---|---|
id/version |
Returns the highest version of the protocol supported. Always SNP/3.1 . |
signalType |
Indicates the signal type. There are two types of signal: CALLBACK and GOODBYE . |
Content
GOODBYE
signals by default do not include any additional content, however SNP 3.1 servers are free to include custom content if they wish; CALLBACK
signals include the following content by default:
Item | Description |
---|---|
callback—number |
The signal type as a number. |
callback-type |
The signal type as text. |
uid |
The notification's uid , if one was provided when it was created. |
data- |
Any custom data items that were provided when the notification was created. The names of these will be prefixed with data- . |
Callback Signal Types
Number | Name | Description |
---|---|---|
303 |
NotificationExpired |
The notification was removed from the display with no user interaction. |
304 |
NotificationInvoked |
The user clicked on the primary action button, if one was provided. |
307 |
NotificationDismissed |
The user dismissed the notification. |
308 |
OptionSelected |
The user selected an option from the list provided. |
309 |
NotificationSnoozed |
The notification was snoozed by the user. |
Example Signals
SNP/3.1 GOODBYE
END
SNP/3.1 CALLBACK
END
Forwarding
Forwarding acts as a compromise between applications not having to register themselves with Snarl and the end user still being protected from unsolicited notifications. A forwarded notification follows a very similar structure to a standard NOTIFY
request; with the following exceptions:
- The request type is set as
FORWARD
- The
source:
field must be populated - The
app-id:
andevent-id:
fields must not be populated
Example
SNP/3.1 FORWARD
source: Weather Alerts
title: Weather Warning
text: Strong winds are forecast for the next 48 hours
icon: !weather-warning
END
Forwarded notifications may be blocked by the end user at a global level, or at a receiver level. The source provided may also be blocked. Consequently, sending applications must check the response received and cease sending further notifications if required.
Subscriptions
A subscription is a request from a client to a server where the client asks to receive notifications from the server. The notifications will be sent as forwarded messages, rather than traditional notifications.
Subscriptions can take two forms: notifications can be forwarded to a URL as an HTTP POST
request, or they can be sent as an SNP 3.1 FORWARD
message to a port on the same computer as the client.
Subscriptions must include a unique identifier (uid) which is used when unsubscribing and should also be sent by the server when it needs to issue a GOODBYE
response.
Examples
Straightforward request which asks for notifications to be sent to port 5000:
SNP/3.1 SUBSCRIBE
uid: my_lame_uid
reply-port: 5000
END
Same, but using authorisation:
SNP/3.1 SUBSCRIBE MD5:ABCDEF1234.9876543210
uid: my_lame_uid
reply-port: 5000
END
Asks for notifications to be sent to http://mysever:4444/bucket/
:
SNP/3.1 SUBSCRIBE
uid: my_lame_uid
forward-to: http://mysever:4444/bucket/
END
Security
Security covers two parts: authorisation and encryption. Authorisation ensures the receiving server trusts incoming messages from a particular client by validating that they both share a common secret (in this case, a password); encryption protects the contents of the message from inspection during it's journey from the client to the server.
Messages can include authorisation without including encryption, however the reverse is not true, as encrypted messages rely on the key created during the authorisation process. So, if you want encryption, you’ll need to also use authorisation.
Authorisation
To authorise a message, the password must be translated into a key hash using a supported hash algorithm and salt (see the Developer Guide for details on how to generate these values). This is all then included in the header, as follows:
SNP/3.1 {action} {hashAlgorithm}:{keyHash}.{salt}
Item | Description |
---|---|
hashAlgorithm |
The hashing algorithm to use. Can be one of MD5 , SHA1 , SHA256 , SHA512 . It is recommended that only SHA256 and SHA512 are used in practice. |
keyHash |
The hash of the generated key in hex-encoded format. |
salt |
The cryptographically secure salt created as part of the key hash generation, in hex-encoded format. |
Example
SNP/3.1 FORWARD MD5:123456789ABCDEF.CA53559F21004566
source: SecureBot
text:Hello, world!
END
Note that authorisation is not the same as authentication. Authentication applies to individual application registrations and ensures that one application cannot spoof notifications from another application.
Encryption
Encrypting a message requires both authorisation and a valid encryption algorithm and initialisation value. The header therefore looks thus:
SNP/3.1 {action} {hashAlgorithm}:{keyHash}.{salt} {encryptionAlgorithm}:{iv}
Item | Description |
---|---|
encryptionAlgorithm |
The encryption algorithm to use. Only AES is currently supported. |
iv |
The encryption initialisation value, in hex-encoded format. |
The encrypted message still has a header and terminating line, however the content is encrypted into a byte array which is then hex-encoded as a single line.
Example
The following is an encrypted SNP 3.1 request. See the SecureSnp demo for more details:
SNP/3.1 NOTIFY SHA256:94C1BB81DC569F0F2AFCCD837645B735597558BA429B78C5484699F46C717638.CF0BDD3932A2D83C2C336840F6DF088D AES:322161C369AD6758DC217887A8FE0C56
B280702277F75E5D253BACCF17EB361373FE71FD774AE42FBCB3F0E4C8BFD44286E3FA2181A46C077AC80DFCC38C312FEEB2BE74477F9E4ACD6D7E047C82E94B7BCEF2B2EA88259516098D037245635418D9CFEB413B9D10E56E7F350A31467C986AC9A7309CC6116628006EEEE309D33AF16F596DBB53861189F01C7D6130318052B376FEEDDD7431732694D3FB255DEDE84A66EB363B87A7959B9A981A71F3
END
Hex-encoding the message content typically doubles the overall size of a message packet compared to the same packet unencrypted. As notifications are by nature intended to be brief, this should not be a significant issue. However, if a client typically transfers a large amount of meta data along with the notification, consideration should be given to using a different mechanism to transfer the data - especially if network bandwidth is a concern.