Proposed Specification - fullphat/snarl_network_protocol GitHub Wiki

Warning 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 artefacts
  • GET methods will retrieve information
  • DELETE methods will delete or remove artefacts
  • Other methods (e.g. PUT) are reserved for possible future use

Content type

  • POST methods must use application/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 and oxide-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 be null.

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, and DELETE /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.