Proposed Specification - fullphat/snarl_network_protocol GitHub Wiki
This is provisional documentation. Applications should not attempt to implement any features listed in this document, other than for development or testing purposes. The documentation is provided for open access to encourage interest in forthcoming features and to solicit feedback and suggestions. It is therefore provided "as is" and is subject to ongoing change and revision.
Purpose
This document is intended to define Version 2 of the Oxide API (previously known as SNP/HTTP). The intention is to create a more usable and extensible protocol that adheres to industry standards, as defined in Key Principles below.
Key Principles
- Backwards-compatible with earlier versions of SNP/HTTP
- Provide the ability to retrieve information from Snarl
- Adopt a RESTful approach
- Support authorisation
- Support secure transmission
- Define standard request and response objects
- Error messages will include helper text wherever possible
- Error messages will use standard HTTP status codes
- Request and response body content will be formatted as JSON
Standards
The following standards are proposed:
Base URI
The base URI for will be /v2
, which allows for backward compatibility with the original SNP/HTTP (V0), and the hybrid V1 version.
Methods
POST
methods will create or update artefactsGET
methods will retrieve informationDELETE
methods will delete or remove artefacts- Other methods (e.g.
PUT
) are reserved for possible future use
Content type
POST
methods must useapplication/json
as the content type- Where required, the request body will always be formatted as JSON
- All responses (except when calling the root endpoint) will be returned as JSON
Headers
- If a password is required, it is passed in the
oxide-password
custom header - If authorisation is required, the custom headers
oxide-auth-type
,oxide-auth-keyhash
andoxide-auth-salt
will be used
Resource Identifiers
- When retrieving information, the resource's guid must be used. This is publicly available (e.g. by calling
GET /apps
). - When creating, updating or deleting, the resource's identifier must be used. This is not publicly available and is usually only known by the application itself.
Authentication
- Retrieving information does not require authentication
- Modifying or deleting resources requires authentication
Responses
Every request generates the same response, which will contain two objects:
-
meta
: indicates whether the request was successful or not, and may contain additional metadata such as the responding server name and response timestamp. If the request was not successful, the meta object may also contain a human-readable explanation of why it failed; -
data
: if the request was successful, information provided in response to the request will be in here. If the request was not successful, this will benull
.
Example Success Response
{
"meta": {
"code": 201,
"text": "Created"
},
"data": {
"guid": "a7fdb4de-e462-4866-89ce-7b4d28929255"
}
}
Example Failure Response
{
"meta": {
"code": 404,
"text": "NotFound",
"reason": "Application 'foo.bar' isn't registered."
},
"data": null
}
Definitions
There is no unified view on how RESTful APIs should present themselves, although there is a general set of rules which many adhere to, either fully or to a greater extent. Below are key definitions used within Oxide:
Endpoints
An Oxide endpoint is a URI path without any query parameters:
/this/is/an/endpoint
/this/is/also/an/endpoint
Services
The Oxide API provides access to a number of services that handle certain features. Just as a company may have Finance, Payroll and Distribution departments, Oxide provides access to Snarl's registrations, notifications and other features.
A service is the first element in the URI path after the base v2
element. Each endpoint has a service as its root; an endpoint may also just be a service. These are hypothetical endpoints of the 'foo' service:
/foo/resources/23
/foo/targets/today
/foo
Managing Resources
Creating New Resources
There are two different approaches here:
Option 1: Hierarchical
This option is the most structured, and the first that springs to mind when defining how resources might be managed, as it follows a traditional root/tree/branch approach with resources structured such that the root contains all application registrations, each application registration contains event classes, and each event class contains notifications.
While this initially appears to be a sensible approach, it quickly results in a complex request structure, with requests fanning out from a single point, as follows:
To register an application:
POST
/v2/registrations/
{ ... }
To add an event class to application 'foo':
POST
/v2/registrations/foo
{ ... }
To create a notification for application 'foo' and event 'bar':
POST
/v2/registrations/foo/events/bar
{ ... }
As can be seen, although registering an application is very simple, creating a notification (undoubtedly the more common action) requires a long URI which also loses the context of the request as each request appears to be creating something against registrations
whereas it's actually creating a notification.
Option 2: Service-oriented (Preferred)
While less intuitive, a service-oriented approach lends itself better. This approach defines a number of 'services' which form the root of the URI after the version identifier. Currently proposed services are:
- Registrations
- Events
- Notifications
From here, a logical approach can still be taken, as follows:
Service | POST |
GET |
DELETE |
---|---|---|---|
Registrations |
Creates or updates an existing app registration | Retreives all registered applications, or information about a single application | Unregisters an application |
Events |
Creates or updates an existing event class | Gets information about all events, or just a single event, for a specific application | Removes an event class |
Notifications |
Creates a new notification | Retrieves information about a notification | Removes a notification from the screen |
Accessing Resources
There are several approaches here:
- In the URI path, for example:
POST
/v2/registrations/foo
- In the URI query, for example:
POST
/v2/registrations?AppId=foo
- In the request body, for example:
POST
/v2/registrations
{ "AppId": "foo" }
The preferred approach is to use the URI path. There are several benefits to this:
- Aligns to most RESTful API approaches
- Aligns to HTTP principles that the URI path defines the resource being accessed
- Intuitive, as the same URI can be used with different methods (e.g.
POST
/v2/registrations/foo
,GET
/v2/registrations/foo
, andDELETE
/v2/registrations/foo
Additionally, even though sensitive information (e.g. the application identifier) is passed in the URI, this is still encrypted if HTTPS is used.
Identifying Resources
Resources within Snarl can be accessed in two ways: using identifiers specified by the application itself, or by using the guids that Snarl assigns.
It is important to note that, while identifiers are stored in clear text by Snarl, they are not normally visible to the end user thereby providing a slight level of obfuscation. The approach therefore is for requests that retrieve information (typically those using GET
) to use the resource's guid as the identifier, and requests that create, update or delete resources to use the resource's identifier. Additionally, requests which create, update or delete resources must include the registration password if one was used originally.