Skip to content

Introduction

Last Updated 2019/07/08

image/svg+xml

NiX is a platform for easily and securely governing access to shared data. By requesting, publishing, denying, and revoking content packages through NiX, you can give your users control over their data. Content packages can contain permissions information, metadata, encryption keys, and more. Through the use of encryption keys, applications can protect and access information as close to the user as possible.

This is documentation for the NiX API itself.

Most readers will use one of our SDKs to interact with the API on your behalf; however, we're not here to tell you how to live your life. If you want to know how go from zero to registered and sending content-packages back and forth across https://api.nix.software at a low and detailed level, you've come to the right place. Otherwise, SDK documentation can be found at sdk.nix.software.

What's Covered

  • The NiX API data model
    • NiX Customers, Applications, and API Keys
    • NiX Identities
    • Content Packages
  • Request and response formats and authorization
  • Registering Identities.
  • Exchanging content packages, modifying access control, and managing NiX-cached packages.

Data Model

All requests through NiX are authenticated and tracked through API Keys. In order to be used, an API Key must be enabled and must not be expired. One ore more API Keys are associated with an Application. Customers may have one or more Applications.

Applications have Identities of various types. Identities are cryptographic entities that can send and receive messages secure mssages to each other. They are used by different components of the system for different purposes. Identities have an address, signing and encryption keys, metadata, and a type representing how the identity is used. A local installation of an application may have many identities. For example, a user identity through which the user makes requests for content packages, a content vault may have a unique identity for communicating directly with it to configure the vault, and be subscribed to a content route identity along with other vaults in order to respond to requests for content packages. Vaults securely store content packages created by other Identities that have the appropriate permissions in that vault. User Identities create and modify content packages within Vaults in which they have appropriate control. Identities may also request content packages from any Vault. The Vault can decide whether or not to return the pacakge based on its configuration and contents. Vaults may pre-publish content packages to the API for caching and may also send revocation reqeusts to clear the cache and notify Identities that they should clear their copy of the content package. Routes are shared identities used to allow multiple vaults to respond to a request.

All messaging between Identities uses end-to-end encryption of the content packages and any control or metadata that is not directly relevant to the service performed by the API. Exceptions to this encryption include addresses and content package identifiers. These are used for routing and for cache operations.

Request and Response Format

All requests and responses are JSON over HTTPS with Authorization headers using API Keys. All endpoints other than the operational endpoints require TLS. The use ot Content-Type headers is encouraged but not required.

With few exceptions, responses follow a common format (with fields being present or missing dependeing on the particular endpoint):

{
  "status":   200,
  "code":     20001,
  "err":      "Errors if applicable",
  "msg":      "Successful message if applicable",
  "txnID":    "1556727543072499643-1920627189",
  "first":    "firstIndexCursor",
  "last":     "lastIndexCursor",
  "continue": true,
  "body": {
    "key": "value"
  }
}
  • status is included in every response. It is an HTTP status code and is usually the same as the HTTP status of the response.
  • code is NiX response code. Regardless of whether an error ocurred or the request was successful, these codes offer more detail about what happened. Error code ranges indicate whether or not the request was successful and/or where the error occurred:
    • 10000 - 19999 are client to client responses via NiX.
    • 20000 - 29999 are successful requests to NiX itself.
    • 40000 - 49999 are client errors (bad request format, unauthorized, etc.)
    • 50000 - 59999 are server errors (database errors, connectivity issues, etc.). These generally will not be too descriptive for security reasons.
    • 60000 - 69999 miscellaneous and otherwise hard-to-categorize errorr codes.
  • err is present only if there was an error. It will contain a description of what went wrong. Errors are sometimes supplemented information in the body.
  • msg is present on successful requests.
  • last and continue are returned on paginated requests. last is an opaque cursor string that should be returned to get the next page of results. continue indicates where or not the server thinks there are likely additional results.
  • body will be present if the request type returns content. It will sometimes also be present on errors to provide additional information about what went wrong.

body may have multiple data types.

The type of the body value is dependent upon the request endpoint and the response type. Some responses may contain objects, while others may contain strings, arrays, or numbers.

Local addresses can exclude the host.

In these examples, full addresses like igQQD@api.nix.software are used, complete with the api.nix.software hostname. If an address is local to the API that you are using, it is valid to drop the @api.nix.software and use only the local adress igQQD. The full hostname will be assumed. In communications, it is important that you only do this if all parties are local to the same API.

Example

Here is an example of registering a new Identity. Note the Bearer Authorization Header which contains a JWT signed by an API Key. Note also the use to JSON Web Signatures - more on that below.

POST /v1/identity HTTP/1.1

POST /v1/identity HTTP/1.1
Host: api.nix.software
Authorization: Bearer eyJhbGciOiJIUzI1NiIsImtpZCI6ImFnUUlCQVFAYXBpLm5peC5zb2Z0d2FyZSJ9.eyJhdWQiOlsiYXBpLm5peC5zb2Z0d2FyZSJdLCJleHAiOjE1NjUyNzgzNDUsImlhdCI6MTU2NTI3ODA0NSwibmJmIjoxNTY1Mjc3NzQ1fQ.zW25oZ_UahjBsbkvmFCUnKA31nWUsTwRZqOhf6z26KY
Content-Type: application/json;charset=UTF-8
{
  "sigPubKey": {
    "use": "sig",
    "kty": "EC",
    "kid": "-SMMgfVKs0NcURTExtj_UsHhoGLNclVdT1xBvFbZGsg",
    "crv": "P-256",
    "x": "LJJiG6NFacqTUuHx6x2hnmTxUjYbgf4YNwAyncw4i5g",
    "y": "wRzWwL8JzFmPIhGeb3jlhC6aJmtkQlQA_pM05K20WQ0"
  },
  "encPubKey": "eyJhbGciOiJFUzI1NiIsImlhdCI6MTU2NTI3ODA0NSwia2lkIjoiLy1TTU1nZlZLczBOY1VSVEV4dGpfVXNIaG9HTE5jbFZkVDF4QnZGYlpHc2ciLCJ0Z3QiOiIvdjEvaWRlbnRpdHkjZW5jUHViS2V5In0.eyJ1c2UiOiJlbmMiLCJrdHkiOiJFQyIsImtpZCI6IjFVVndSVHV6bmF6eXU2T1FSa1lKM3B4Wjl0eUZhdTRoTmp1aFZJZmhSOGsiLCJjcnYiOiJQLTI1NiIsIngiOiItcUp5N3JEMWY3Q1IxZGRrRjBJRmh2LVNxMVRTVlZUWEFfTkVTb1FVVGFRIiwieSI6Ik1wS1kyM1dBX2oyV3JZeXBVYzQ0MVdqRDgzOFVZUXp6aGdlVTRxMEZlU2MifQ.KWlnhd98gxL6iMsQxcUk8tV2LxxtQyckKT0VM-hC7heBmCXIZWf4YU9yWP6lmZ8cn3wzBZMkvQLNdUDFdMKf2w",
  "metaPublic": "eyJhbGciOiJFUzI1NiIsImlhdCI6MTU2NTI3ODA0NSwia2lkIjoiLy1TTU1nZlZLczBOY1VSVEV4dGpfVXNIaG9HTE5jbFZkVDF4QnZGYlpHc2ciLCJ0Z3QiOiIvdjEvaWRlbnRpdHkjbWV0YVB1YmxpYyJ9.eyJpZGVudGl0eVR5cGUiOiJ1c2VyIn0.3eVaqUWwsg7BRqDlYxn9vq_LFD0WQWaO7oFLn8MLu-O9aEJpO0AmtlpTK7HZsEOxmXjV26VqG4ZWpkL74URe_Q",
  "metaPrivate": "eyJhbGciOiJFUzI1NiIsImlhdCI6MTU2NTI3ODA0NSwia2lkIjoiLy1TTU1nZlZLczBOY1VSVEV4dGpfVXNIaG9HTE5jbFZkVDF4QnZGYlpHc2ciLCJ0Z3QiOiIvdjEvaWRlbnRpdHkjbWV0YVByaXZhdGUifQ.e30.uQCrbIfgc-_uooYsoPqibBDHOdCGUKXfCTuHZbHC34mjja3GvtXqU1Rb-kMmYLIToPL5polY66rsWJRAC9UxEw"
}

Decoded encPubKey - JWS

{
  "payload": {
    "crv": "P-256",
    "kid": "1UVwRTuznazyu6OQRkYJ3pxZ9tyFau4hNjuhVIfhR8k",
    "kty": "EC",
    "use": "enc",
    "x": "-qJy7rD1f7CR1ddkF0IFhv-Sq1TSVVTXA_NESoQUTaQ",
    "y": "MpKY23WA_j2WrYypUc441WjD838UYQzzhgeU4q0FeSc"
  },
  "protected": {
    "alg": "ES256",
    "iat": 1565278045,
    "kid": "/-SMMgfVKs0NcURTExtj_UsHhoGLNclVdT1xBvFbZGsg",
    "tgt": "/v1/identity#encPubKey"
  },
  "signature": "KWlnhd98gxL6iMsQxcUk8tV2LxxtQyckKT0VM+hC7heBmCXIZWf4YU9yWP6lmZ8cn3wzBZMkvQLNdUDFdMKf2w=="
}

Decoded metaPublic - JWS

{
  "payload": {
    "identityType": "user"
  },
  "protected": {
    "alg": "ES256",
    "iat": 1565278045,
    "kid": "/-SMMgfVKs0NcURTExtj_UsHhoGLNclVdT1xBvFbZGsg",
    "tgt": "/v1/identity#metaPublic"
  },
  "signature": "3eVaqUWwsg7BRqDlYxn9vq/LFD0WQWaO7oFLn8MLu+O9aEJpO0AmtlpTK7HZsEOxmXjV26VqG4ZWpkL74URe/Q=="
}

Decoded metaPrivate - JWS

{
  "payload": {},
  "protected": {
    "alg": "ES256",
    "iat": 1565278045,
    "kid": "/-SMMgfVKs0NcURTExtj_UsHhoGLNclVdT1xBvFbZGsg",
    "tgt": "/v1/identity#metaPrivate"
  },
  "signature": "uQCrbIfgc+/uooYsoPqibBDHOdCGUKXfCTuHZbHC34mjja3GvtXqU1Rb+kMmYLIToPL5polY66rsWJRAC9UxEw=="
}

Response

HTTP/1.1 200 OK
Content-Type: application/json
Date: Thu, 08 Aug 2019 15:27:25 GMT
Content-Length: 1010
Access-Control-Allow-Headers: Authorization, Content-Type
Access-Control-Allow-Methods: *
Access-Control-Allow-Origin: *

{
  "status": 200,
  "code": 20011,
  "msg": "Successfully registerd identity.",
  "txnID": "1565278045458655771-884253640",
  "body": {
    "address": "igQIN@api.nix.software",
    "sigPubKey": {
      "use": "sig",
      "kty": "EC",
      "kid": "-SMMgfVKs0NcURTExtj_UsHhoGLNclVdT1xBvFbZGsg",
      "crv": "P-256",
      "x": "LJJiG6NFacqTUuHx6x2hnmTxUjYbgf4YNwAyncw4i5g",
      "y": "wRzWwL8JzFmPIhGeb3jlhC6aJmtkQlQA_pM05K20WQ0"
    },
    "encPubKey": "eyJhbGciOiJFUzI1NiIsImlhdCI6MTU2NTI3ODA0NSwia2lkIjoiLy1TTU1nZlZLczBOY1VSVEV4dGpfVXNIaG9HTE5jbFZkVDF4QnZGYlpHc2ciLCJ0Z3QiOiIvdjEvaWRlbnRpdHkjZW5jUHViS2V5In0.eyJ1c2UiOiJlbmMiLCJrdHkiOiJFQyIsImtpZCI6IjFVVndSVHV6bmF6eXU2T1FSa1lKM3B4Wjl0eUZhdTRoTmp1aFZJZmhSOGsiLCJjcnYiOiJQLTI1NiIsIngiOiItcUp5N3JEMWY3Q1IxZGRrRjBJRmh2LVNxMVRTVlZUWEFfTkVTb1FVVGFRIiwieSI6Ik1wS1kyM1dBX2oyV3JZeXBVYzQ0MVdqRDgzOFVZUXp6aGdlVTRxMEZlU2MifQ.KWlnhd98gxL6iMsQxcUk8tV2LxxtQyckKT0VM-hC7heBmCXIZWf4YU9yWP6lmZ8cn3wzBZMkvQLNdUDFdMKf2w",
    "metaPublic": {
      "identityType": "user",
      "appAddress": "AgQIB@api.nix.software"
    },
    "metaPrivate": {
      "created": "1565278045",
      "lastHeardDay": "1565278045"
    }
  }
}

In many examples, you will see the formatted HTTP request and responses with contents signed and encrypted as they would normally be in the request body. There may also be additional boxes containing decoded and reformatted portions from the request to add clarity.

Cryptographic Message Formats

The NiX platform is built on the use of cryptographic primitives such as a simple public key infrastructure, digital signatures, and encryption. To keep messages predictable and uniform, increase portability, encourage reuse of existing well-tested libraries, and avoid re-inventing the wheel, all requests and responses are built on the JSON Web Signing and Encryption set of IETS RFCs.

Note

Previously JOSE and JWTs in particular have been derided as insecure in popular media. It is important to understand that these technologies are neither fundementally secure nor insecure. Achieving security depends on careful implementation. Practitioners must choose and restrict ciphers, signing algorithms, key negotiation algorithms, elliptic curve parameters, etc. purposefully. It is equally vital to validate all inputs and make sure that the expected restrictions are met before attempting any decryption or validation.

Cryptographic Requirements

  • Asymmetric Signatures: ECDSA with SHA-256 using the NIST P-256 curve (ES256 in JWA).
  • Asymmetric Encryption: ECDH-ES (Elliptic Curve Diffie-Hellman Ephemeral Static key agreement) using the NIST P-256 curve with AES 256 key wrapping with AES 256 GCM for authenticated content encryption (ECDH-ES+A256KW in JWA and A256GCM in JWA).
  • Public keys should generally be expressed as JSON Web Keys (use of the crv, x, y parameters is preferred over the x5* certificate representation).
  • When creating a JSONWebKey, create a key ID kid unique to that key so that signed or encrypted data can reference which key should be used to validate or decrypt it by key ID. The recommended choice used in these examples is the base64-URL-encoded SHA256 thumbprint of the key as defined in RFC7368. Key IDs must contain only the base64 URL-safe character set.
  • Signatures and encrypted content should be expressed in compact form unless otherwise indicated on the specific endpoint. Some APIs may tollerate JSON form, but this should not be relied upon unless indicated.
  • Signature headers and encryption headers should contain the Key ID (kid) rather than the actual key (jwk). This kid will be the address of the source (JWS) or destination (JWE) communicator (identity) if known, a slash /, and the kid of the public signing or encryption key.
  • In JWTs intended for NiX, the audience restriction (aud) should be api.nix.software.
  • In JWTs, the expiry (exp) and not before (nbf) times should be set and should be valid.
  • In cases where keys are derived from passphrases, PBKDF2 is used with SHA256 and 10,000 rounds.

Forward Secrecy in NiX

Under the ECDH-ES scheme, each message is encrypted with a unique key that is derived from an ephemeral key pair generated by the sending instance and the current published public side of the receiving instance's encryption key pair. This achieves a form of forward secrecy in that if any key used to encrypt a message is broken, the recovered key cannot be used to easily decrypt any other message or recover any other key. If the private key within the encryption key pair of the receiving instance is stolen, all messages encrypted with keys derived using that private key are compromised. In order to achieve Perfect Forward Secrecy, the encryption key pair associated with an instance would need to be rotated after every encrypted message. Given the asynchronous nature of the system and the need to reduce round trips to control latency and conserve bandwidth, the encryption pair is not rotated at every message; however, it is recommended that this pair be rotated frequently. When requesting a content package, request-specific ephemeral encryption pairs can be generated and used; however, a cached response may be returned using a previous key.

Request Endpoints

The following sections contain a partial listing of important HTTP endpoints available in the API.

Identity Endpoints

POST /v1/identity

Register a new Identity.

POST /v1/identity/me

Get all information (signing and encryption public keys, signature chain, metadata) about the current identity.

POST /v1/identity/me/sigPubKey

Update the public signing key for the current Identity. This requires a signature over the new key using the previous private key.

POST /v1/identity/me/encPubKey

Update the public encryption key for the current Identity. This requires a signature over the new key using the current signing private key.

POST /v1/identity/me/meta

Update the private metadata associated with this Identity. This requires a signature over the new metadata using the current signing private key.

POST /v1/identity/me/recovery

Update the public recovery data associated with this Identity. This requires a signature over the recovery data using the current signing private key. Recovery data should be encrypted client-side with an external key or key derived from a passphrase or other external source.

GET /v1/identity/:id/recovery

Get recovery data, decrypt it to recover access to the Identity.

GET /v1/identity/:id

Get the public information (signing and encryption public keys, signature chain) about an Identity.

Message Endpoints

POST /v1/messages/send

Send one or more messages from an Identity to another Identity. Messages will be delivered synchronously or asynchronously as needed and will be cached until the specified expiration time.

POST /v1/messages/fetch

Fetch cached messages from other Identities. Messages are returned most recent first and can be paginated backward in time.

POST (LONG POLL) /v1/messages/listen

This endpoint will return a stream of messages starting with the most recently cached messages followed by a live stream of any new messges that are sent. Messages should be able to be deduplicated by sender and message ID.

GET (WEB SOCKET) /v1/messages/ws

This endpoint will return a stream of messages starting with the most recently cached messages followed by a live stream of any new messges that are sent. Messages should be able to be deduplicated by sender and message ID. The web socket also supports sending messages.

Event Stream

GET /v1/customer/:cust/stream/events

Get metadata about recent events occurring on the platform.

GET /v1/customer/:cust/stream/events/listen

Listen to a filtered stream of metadata about events occurring on the platform.

Operational Endpoints

These endpoints provide status information about the software. They typically aren't used by clients.

GET /ops/health

Returns 200 if the service is operating normally.

GET /ops/ready

Returns 200 when the server has completed initialiization and self-testing. No service instance should be externally visible until this endpoint is returning 200.

GET /ops/version

Returns the specific build of the service being used.

GET /ops/licenses

Returns a list of all packages used by the NiX API and their licensing terms.