Documentation

Getting started

A few links to get you going quickly:

Storing and reading configuration

Webhooks

Security model

We operate on a zero-trust principle. Every document stored in Occson is encrypted on the client-side. This means that neither the document’s plaintext contents nor the document password ever reach our servers. We cannot leak them - either accidentally or on purpose. We also cannot be forced to divulge them to a nation-state, law enforcement, or a bad actor.

Your encrypted content is, however, accompanied by minimal metadata. We can tell when it was created, when it was last updated, and who owns the workspace within which it resides. We do not believe this to be a security issue, but we present this information openly in the interest of transparency - so you can decide whether Occson is right for you.

Encryption

We use AES-256 for encryption as described in NIST FIPS 197 in CBC mode. As far as is publicly known, this form of encryption has been approved for use by the U.S. military on documents classified “top secret”. As of today, there are no known to the general public, reliable attacks against this encryption algorithm, though they have been attempted and we are certain it will continue to be so. aes-everywhere provides compatible implementations in many programming languages.

In transit your data is secured by SSL, both on the web application and when a client talks to our API. We do not allow unencrypted connections to our servers. This provides a reasonable level of protection from eavesdroppers - in effect the document contents are wrapped in two layers of encryption. While an attacker at ISP level will be able to glean some metadata - that you’re connecting to Occson’s servers and when - they will not be able to tell what paths you are accessing.

Web application

In the case of our web application, we use JavaScript to encrypt the document with the given password right in the browser. Only the result of that encryption is sent over the Internet and stored on our servers. We never see the plaintext document contents nor the password. We do not run any invasive trackers within the app, so while you do need to have JavaScript enabled to use the document uploader, there is no third party that could look over your shoulder while you type.

We don’t use a CDN for the JavaScript we ask you to run, either, as these could be hijacked. It performs a simple purpose - data encryption - and so we can keep it small enough to serve it directly from our servers.

For every other part of our web application, JavaScript is optional and it will work just fine if you disable it.

CLI

Our CLI is written in Ruby and completely open-source. You can read the source code on GitHub. As is customary with Ruby gems, occson gem is hosted on rubygems.org. However, since Ruby is an interpreted language, you can verify that the code you downloaded is the same thing we published.

Both the web application and CLI use the same document format and encryption algorithm, so documents created using either are completely interoperable.

occson also performs encryption on client-side only. The document contents and passwords never leave your machine.

Custom clients

Our API is open and publicly specified (see below). It is therefore possible to build a custom client that communicates with it. We do not discourage doing so, as we believe in the ability to interact with your data as you see fit. However, do note that we do not offer support with building such clients, ensuring they interoperate with our offerings, or auditing their security.

REST API

Document object definition

We define a document’s encrypted content as follows:

  • Characters 0 - 7: the string Salted__
  • Characters 8 - 15: the document salt, 8 characters long
  • Characters 16 - n: the ciphertext

For transit to the server, the entire string is then base64-encoded.

A reference example of the salting and encryption process, as performed in Ruby, can be found here.

Authentication & authorization

All requests made to the REST API must be accompanied with an Authorization header. It is expected to take the form of:

Token token=[your access token]

Where [your access token] is replaced by the appropriate token, for example:

Token token=decafc0ffeebad

Workspace is selected based on the token itself; none of the request paths contain a workspace part. A token has an access level assigned through the web app. A read token will only allow using the GET endpoints.

GET /:path

Get the details of a document denoted by :path in the workspace specified by the token accompanying the request.

URL: https://api.occson.com/:path

Method: GET

Auth required: yes, token

Token access level required: READ

Success response

Code: 200 OK

Content examples

{
  "id": "a24f72bb-684d-41da-b7a6-180aba7b637c",
  "path": "/some/document/path.ext",
  "encrypted_content": "U2FsdGVkX184NzY1NDMyMS4uLg==",
  "workspace_id": "d43d04d4-5c5b-472d-9b65-2802132bce70",
  "created_at": "2020-12-05T14:15:39.245Z",
  "updated_at": "2020-12-05T14:15:39.245Z"
}

Error responses

Code: 403 Forbidden

Typical reason: The token passed as authorization is disabled.

Example

{
  "message": "Forbidden",
  "status": "403"
}

Code: 404 Not Found

Typical reason: The requested path could not be found in the workspace. Check your token and path.

Example

{
  "message": "Not found",
  "status": "404"
}

Code: 402 Payment Required

Typical reason: Access to the workspace through the API was restricted due to it not being included in the free plan.

Example:

{
  "message": "The subscription for this workspace has lapsed",
  "status": "402"
}

POST /:path

Write a document to the workspace associated with the auth token, at :path. Will fail if the path already exists unless the force parameter is given.

URL: https://api.occson.com/:path

Method: POST

Auth required: yes, token

Token access level required: WRITE

Params

  • force: if given and contains the string true, the endpoint will allow overwriting a document if one already exists at the given path.

Data example

{
  "encrypted_content": "U2FsdGVkX184NzY1NDMyMS4uLg=="
}

Success response

Code: 201 OK if document newly created, 200 OK if document updated.

Content examples

{
  "id": "a24f72bb-684d-41da-b7a6-180aba7b637c",
  "path": "/some/document/path.ext",
  "encrypted_content": "U2FsdGVkX184NzY1NDMyMS4uLg==",
  "workspace_id": "d43d04d4-5c5b-472d-9b65-2802132bce70",
  "created_at": "2020-12-05T14:15:39.245Z",
  "updated_at": "2020-12-05T14:15:39.245Z",
}

Error responses

Code: 403 Forbidden

Typical reason: The token passed as authorization is disabled or does not have write permissions.

Example

{
  "message": "Forbidden",
  "status": "403"
}

Code: 409 Conflict

Typical reason: A document already exists at this path and force parameter is not set to true.

Example

{
  "message": "Conflict",
  "status": "409"
}

Code: 402 Payment Required

Typical reason: Access to the workspace through the API was restricted due to it not being included in the free plan, or the workspace is not on a paid plan and has exceeded the limit of 100 documents.

Examples

{
  "message": "The subscription for this workspace has lapsed",
  "status": "402"
}
{
  "message": "Free workspaces are limited to 100 documents",
  "status": "402"
}

Webhooks

Webhooks are configured through Occson UI.

Creating a webhook

Whenever a document under the webhook’s workspace changes, a POST request is produced to the given endpoint with:

  • X-Occson-Signature header containing a HMAC-SHA256 signature of the payload and timestamp
  • X-Occson-Timestamp containing the timestamp of signing
  • POST body containing a JSON formatted like the following:
{
  "id": "00000000-0000-0000-0000-000000000000",
  "path": "/test",
  "workspace_id": "00000000-0000-0000-0000-000000000000",
  "event": "created|updated|destroyed"
}

Where:

  • ID is the internal ID of the changed document
  • Path is the given path of the document
  • Workspace ID is the internal ID of the workspace where the change occured
  • Event is one of created, updated, destroyed

See an example on our blog.

CLI

Our CLI is written in Ruby and available as a gem.

Installation

Ensure you have a reasonably modern Ruby on your system - >= 2.7 should suffice. After that, run:

gem install occson

We encourage you check the checksum for the installed gem against the RubyGems page for the gem, located at https://rubygems.org/gems/occson, and compare the downloaded source with the copy at https://github.com/occson/occson.rb.

Usage

The most up-to-date usage information is located in the Usage section of the occson gem README file.

Client libraries

Ruby

The occson gem mentioned above exposes a programmatic interface as well. Adding it to your project can be done in the Gemfile:

gem 'occson', '~> 4.0'

We encourage you to check the latest version on https://rubygems.org/gems/occson and pin it in your project. We observe semantic versioning, so pinning at an appropriate level will protect you from breaking changes in the API.

Documentation for the occson gem is available at https://www.rubydoc.info/gems/occson. It also accompanies the source code and can be generated locally using Yard - please see Documentation section of the gem’s readme file for more information.

Python

The PyPi package occson is the official programmatic interface to Occson for Python. Install with:

pip3 install occson

Sources for the package are located on GitHub.

Golang

The Go package occson is the official programmatic interface to Occson for Golang. We recommend you import as:

import (
	occson "github.com/occson/go-occson"
)

Then run:

go get

Please check out the package documentation for examples. Source repository is located on GitHub.