NAV Navbar
javascript python ruby java php go swift

Introduction

TozStore makes application-level, end-to-end cryptography easy for developers. Most devs aren’t trained in cryptography, so TozStore provides devs with familiar, easy to master tools that run complex, powerful cryptographic operations under the hood. TozStore handles those areas of cryptography that are easy to get wrong (e.g. key generation, algorithms, modes). Whether you are new to cryptography or already highly experienced in implementing crypto, this guide will quickly have you comfortable using TozStore to drastically improve the security of your applications and establish a trustworthy way to write, read, and share data.

Strength of Application-Level Encryption

TozStore provides application-level encryption. Systems that take advantage of encryption to strengthen security typically use infrastructure-level encryption and not application-level encryption. Application-level encryption provides greater application security than infrastructure-level encryption.

With infrastructure-level encryption, networking or database infrastructure is encrypted, so data is encrypted when it is in this infrastructure and not encrypted when it is outside this infrastructure. Infrastructure-level encryption approaches like Virtual Private Networks (VPNs) protect data in transit and can control access to a network. Anyone with access to the network, has access to the services and all data within those services. With infrastructure-level encryption, data will no longer be secure if data leaks outside of the network infrastructure or an attacker bypasses standard access control mechanisms.

TozStore provides application-level security that secures data for its entire life-cycle. Data gets encrypted where it is generated (written) and decrypted where it is consumed (read), providing end-to-end encryption. TozStore encrypts data records individually, providing powerful and fine-grained access control for sharing and revoking access to records. As result, data carries its own security. No matter what infrastructure it crosses, data remains encrypted and secure. Even if data leaks outside of the network infrastructure, gets backed up to an insecure location, or is hacked by an online adversary, the encrypted data remains secure.

For Devs New To Crypto

You do not need to study cryptography in order to integrate strong cryptography in your applications. TozStore will allow you to properly implement strong cryptography with developer-friendly tools that provide a layer of higher-level abstraction over complex cryptographic operations. Devs who are not familiar with cryptography will likely want to know first and foremost how they can trust both TozStore and their own use of TozStore tools to properly implement cryptography in their applications.

Getting Started

Endpoints as TozStore Clients

As a developer using TozStore, you first need to determine what constitutes an end-point in your system. You can install the necessary TozStore SDKs (software development kits) to gain access to TozStore tools. TozStore currently provides SDKs for the following languages: JavaScript for the browser, back-end JavaScript (Node), Python, Ruby, Go, plain Java, Java for Android, Swift,and PHP.

TozStore understands endpoints as clients. Every TozStore client needs a unique set of client credentials that must be generated and provided securely as parameters to instantiate a client instance using an SDK. The client instance possesses read, write, share, revoke, and other methods.

A client's credentials are integrally involved when they perform cryptographic operations, prime examples being when client writes (encrypts), reads (decrypts), and shares access to data records. Because of this, compromised private keys pose a great security risk, and using TozStore properly requires strong key management.

Client Credentials And Key Management

Key management is a foundational security concern while using TozStore. Most significantly, client credentials include a private key that is critical for security. Making a private key public compromises the security provided by TozStore the same way leaving a key in a lock would undermine the security provided by the lock. Losing access to keys also poses the risk of losing access to data, as data encrypted with a client's credentials cannot be decrypted without the relevant client credentials.

The security of data stored using TozStore depends upon the key management practices used surrounding TozStore tools. The section on Key Management in this guide offers advice on making a key management plan. In this section, the team at Tozny who created the TozStore system will share some of our concerns, best practices, and decisions regarding key management. Hopefully, this will help you confidently and quickly decided on the key management plan that is right for your system.

Once you have decided on a way to manage your client credentials, TozStore provides a few ways to generate client credentials. Clients have the option of whether to store an encrypted back-up of their credentials on the server. In choosing a workflow for registering clients, consider the balance between concerns over the security of private keys and the ability to recover lost private keys.

Generating Client Credentials

Regardless of how you choose to generate client credentials and register clients, you will need to use the Tozny Dashboard, a collection of Tozny tools. Create a free account with Tozny. Save this link for easy, future use.

The first way to generate client credentials is go to the Dashboard then Clients tab and click the action button to create a new client, then gather the client's credentials presented as a JSON. In this one action, the Dashboard will generate the needed keys, register the client with TozStore and receive the resulting TozStore client ID and credentials needed to authenticate with the Tozny API. Additionally an encrypted back-up of the client's credentials in TozStore. To avoid having a backup created read on for how to create clients programatically below.

This same workflow can be implemented using a TozStore SDK to dynamically generate client credentials, register, and back-up clients. However, you may be less concerned with being able to retrieve lost private keys and more concerned with stronger security. In this case, you may want a client's private key to only ever be available locally, so you will not want to back-up client credentials in TozStore. In this case, you can dynamically generate client credentials and register clients without backing up client credentials in TozStore.

Dynamically generating client credentials using a TozStore SDK requires first generating a client registration token using the Dashboard. When you generate client credentials and register clients with the Dashboard, your Tozny account username and password enable access to the Tozny API. A client registration token stands in for this layer of authentication.

Overview of Client Credentials

A developer does not need to understand the role client credentials play in allowing a TozStore client to encrypt, decrypt, and share data securely to use TozStore. However, a basic understanding client credentials and the way they are generated may be useful for some developers who want to understand how TozStore works and what they are trying to achieve. Beyond generating a full set of client credentials and passing those client credentials in a secure way to an client instance using a TozStore SDK, you will not need to directly interact with client credentials except for TozStore client IDs.

A full set of client credentials will include: a human-readable name, a unique TozStore client ID, a public and private key-pair used to encrypt and decrypt data, a public and private key-pair used to sign data and validate a signature, an api url, and a pair of api credentials used to authenticate calls made to the TozStore API.

An example of a json containing a full set of TozStore client credentials looks like this:

{ "version": 1, --> version 1 or 2 (1 is only for early TozStore users) "api_url": "https://dev.e3db.com", --> api url the client will use for requests "api_key_id": , --> enables authentication to make TozStore API calls "api_secret": , --> enables authentication to make TozStore API calls "client_id": client's unique TozStore client ID "client_email": , --> not currently used "public_key": , --> client's public data encryption key "private_key": , --> client's private data encryption key KEEP SECURE "public_sign_key": , --> client's public signing key "private_sign_key": --> client's private signing key KEEP SECURE }

The underlying workflow for generating a full set of client credentials includes three stages:

1) A client name and api url are provided as strings, and TozStore SDK methods are used to generate a public key and private key-pair and a public signing key and private signing key-pair.

2) A TozStore SDK client.register method sends the above, partial client credentials to TozStore, and TozStore sends back a unique a client ID, an API key, and an API secret.

3) If desired, the now full set of client credentials are encrypted and backed up in TozStore.

Once a full set of client credentials is generated and a client is registered in TozStore, a client instance can be instantiated using a TozStore SDK to give access to TozStore tools, including methods to write, read, and share data.

Front-End TozStore Clients & Public TozStore Forms

Data Record Structures

Next, you need to determine what structure would be best for your data records. Data records consist of plain-text metadata, plain-text keys, and encrypted values. Plain-text metadata allows querying encrypted data sets.

Records also have a type. By default, data is only accessible by the client that wrote the record. The share method grants other clients access to data of a given type. Shared access to a data type can also be revoked. A record's type provides a way to batch records for granting access between clients.

TozStore

Install and Import Tozny SDK

  // Run the following command in your terminal. 

  $ npm install --save e3db

  // `npm` will update your `package.json`.


  // Alternatively: 

  // Add `e3db` as a dependency in your package.json.

  "dependencies": {
    "e3db": "^1.2"
  }

  // Then run the install command in your terminal. 

  $ npm install

  # With Pip, in terminal:
  $ pip install e3db
  # Add this line to your application's Gemfile:

  gem 'e3db'

  # And then execute:

  $ bundle

  # Or install it yourself as:

  $ gem install e3db

  # At runtime, you will need the libsodium cryptography library.
  # Libsodium is required by the native RbNaCl Ruby library.
  # On most platforms a package is available by default:

  # (Mac OS X)
  $ brew install libsodium    
  # (Ubuntu)
  $ apt-get install libsodium-dev  

  #For libsodium installation instructions for Windows, see the libsodium web site.

  # Windows Users: Make sure to download a recent "MSVC" build. 
  # Once downloaded, find the most recent libsodium.dll inside the ZIP file.
  # Rename it to sodium.dll and copy it to C:\usr\local\lib. 
  # You can also copy it to your \Windows\System32 directory.

  // ANDROID

  // The E3DB SDK targets Android API 16 and higher. 
  // To use the SDK in your app, add it as a dependency to your build. 

  // In Gradle, use:
  repositories {
    maven { url "https://maven.tozny.com/repo" }
    maven { url "https://dl.bintray.com/terl/lazysodium-maven" }
  }

  implementation ('com.tozny.e3db:e3db-client-android:4.0.0-RC2@aar') {
      transitive = true
  }

  // The SDK contacts Tozny's E3DB service.
  // Your application also needs request INTERNET permissions.

  // PLAIN JAVA 

  // For use with Maven, declare the following repository and dependencies:

  <repositories>
    <repository>
      <id>tozny-repo</id>
      <name>Tozny Repository</name>
      <url>https://maven.tozny.com/repo</url>
    </repository>
  </repositories>

  <dependencies>
    <dependency>
      <groupId>com.tozny.e3db</groupId>
      <artifactId>e3db-client-plain</artifactId>
      <version>4.0.0-RC2</version>
    </dependency>
  </dependencies>


  // To install with composer add the following to your composer.json file:

  "require": {
      "tozny/e3db": "1.1.0"
  }

  // Then run: 

  $ php composer.phar install

  // If your project uses Glide for managing dependencies and reproducible builds: 

  // Add the E3DB client library to your glide.yaml by running:

  $ glide get github.com/tozny/e3db-go

  // If you do not use Glide and want to depend on the latest version of E3DB:

  // check out the repository to the correct location within $GOPATH 
  // Then install dependencies using Glide.

  git clone https://github.com/tozny/e3db-go $GOPATH/src/github.com/tozny/e3db-go
  cd $GOPATH/src/github.com/tozny/e3db-go
  glide install

  // E3db is available through CocoaPods. 
  // To install it, simply add the following line to your Podfile:

  pod "E3db", :git => 'https://github.com/tozny/e3db-swift'

TozStore currently provides SDKs for the following languages: JavaScript (Node), Python, Ruby, Go, Java, Swift,and PHP. Our libraries are open source and available here:

PHP JavaScript / Node Python Ruby GoLang Swift Java

Register Client using Tozny Dashboard

You can create clients directly in the Tozny Dashboard. You can simply grab the client credentials from the console. Clients registered from within the console will automatically back their credentials up to your account. You can also create clients dynamically using a Tozny SDK and optionally back-up client credentials.

Register Client With Back-Up (SDK)

  const e3db = require('e3db')

  let token = '...'
  let clientName = '...'

  async function main() {
    let cryptoKeys  = await e3db.Client.generateKeypair();
    let signingKeys = await e3db.Client.generateSigningKeypair();
    let clientInfo  = await e3db.Client.register(token, clientName, cryptoKeys, signingKeys, true)

    // ... Run operations with the client's details here
  }
  main()

  import e3db

  token = '...'
  client_name = '...'

  public_key, private_key = e3db.Client.generate_keypair()

  client_info = e3db.Client.register(token, client_name, public_key, private_key=private_key, backup=True)

  # Now run operations with the client's details in client_info
  token = '...'
  client_name = '...'

  public_key, private_key = E3DB::Client.generate_keypair
  client_info = E3DB::Client.register(token, client_name, public_key, private_key, true)
  $token = '...';
  $client_name = '...';

  list($public_key, $private_key) = \Tozny\E3DB\Client::generate_keypair();
  $client_info = \Tozny\E3DB\Client::register($token, $client_name, $public_key, $private_key, true);

  token := ""
  client_name := ""

  public_key, private_key, _ := e3db.GenerateKeyPair()
  client_info, _ := e3db.RegisterClient(token, client_name, public_key, private_key, true, "https://api.e3db.com")
  // Not yet implemented in TozStore Swift SDK.

Use the Tozny Dashboard to create a client registration token. With this token, you can dynamically create clients with with any of our SDK's. (To generate clients without backing up credentials, see the next section.)

The private key must be passed to the registration handler when backing up credentials as it is used to cryptographically sign the encrypted backup file stored on the server. The private key never leaves the system, and the stored credentials will only be accessible to the newly-registered client itself or the account with which it is registered.

Register Client Without Back-Up (SDK)

  const e3db = require('e3db')

  let token = '...'
  let clientName = '...'

  async function main() {
    let cryptoKeys  = await e3db.Client.generateKeypair();
    let signingKeys = await e3db.Client.generateSigningKeypair();
    let clientInfo  = await e3db.Client.register(token, clientName, cryptoKeys, signingKeys, false)

    // ... Run operations with the client's details here
  }
  main()

  import e3db

  token = '...'
  client_name = '...'

  public_key, private_key = e3db.Client.generate_keypair()

  client_info = e3db.Client.register(token, client_name, public_key)

  # Now run operations with the client's details in client_info

  token = '...'
  client_name = '...'

  public_key, private_key = E3DB::Client.generate_keypair
  client_info = E3DB::Client.register(token, client_name, public_key)
  $token = '...';
  $client_name = '...';

  list($public_key, $private_key) = \Tozny\E3DB\Client::generate_keypair();
  $client_info = \Tozny\E3DB\Client::register($token, $client_name, $public_key);

  token := ""
  client_name := ""

  public_key, private_key, _ := e3db.GenerateKeyPair()
  client_info, _ := e3db.RegisterClient(token, client_name, public_key, "", false, "https://api.e3db.com")

  import E3db

  // This is the main client performing E3db operations
  // (for the remaining examples, we'll assume a non-optional client instance)
  var e3db: Client?

  Client.register(token: e3dbToken, clientName: "ExampleApp") { result in
      switch result {

          // The operation was successful, here's the configuration
          case .success(let config):
              // create main E3db client with config
              self.e3db = Client(config: config)

          case .failure(let error):
              print("An error occurred attempting registration: \(error).")
          }
      }
  }

Use the Tozny Dashboard to create a client registration token. With this token, you can dynamically create clients with with any of our SDK's. (To generate clients backing up credentials, see the previous section.)

The object returned from the server contains the client's UUID, API key, and API secret (as well as echos back the public key passed during registration). It's your responsibility to store this information locally as it will not be recoverable without credential backup.

Load Client Credentials (Configuration) and Create Client Instance

/* Configuration is managed at runtime by instantiating an e3db.Config object with your client's credentials. */

const e3db = require('e3db')

/**
 * Assuming your credentials are stored as defined constants in the
 * application, pass them each into the configuration constructor as
 * follows:
 */

let config = new e3db.Config(
  process.env.CLIENT_ID,
  process.env.API_KEY_ID,
  process.env.API_SECRET,
  process.env.PUBLIC_KEY,
  process.env.PRIVATE_KEY,
  process.env.API_URL
)

/**
 * Pass the configuration when building a new client instance.
 */
let client = new e3db.Client(config)

import e3db
import os

# Assuming your credentials are stored as defined constants in the
# application, pass them each into the configuration constructor as
# follows:

config = e3db.Config(
    os.environ["client_id"],
    os.environ["api_key_id"],
    os.environ["api_secret"],
    os.environ["public_key"],
    os.environ["private_key"]
)

# Pass the configuration when building a new client instance.

client = e3db.Client(config())

require 'e3db'
config = E3DB::Config.default
client = E3DB::Client.new(config)

# The E3DB Command-Line Interface allows you to register 
# and manage multiple keys and credentials using profiles.
# To register a new client under a different profile:

$ e3db register --profile=development developers@mycompany.com

# You can then load a specific profile inside your Ruby application:

config = E3DB::Config.load_profile('development')
client = E3DB::Client.new(config)

/**
 * Assuming your credentials are stored as defined constants in the
 * application, pass them each into the configuration constructor as
 * follows:
 */
$config = new \Tozny\E3DB\Config(
  CLIENT_ID,
  API_KEY_ID,
  API_SECRET,
  PUBLIC_KEY,
  PRIVATE_KEY,
  API_URL
);

/**
 * Pass the configuration to the default coonection handler, which
 * uses Guzzle for requests. If you need a different library for
 * requests, subclass `\Tozny\E3DB\Connection` and pass an instance
 * of your custom implementation to the client instead.
 */
$connection = new \Tozny\E3DB\Connection\GuzzleConnection($config);

/**
 * Pass both the configuration and connection handler when building
 * a new client instance.
 */
$client = new \Tozny\E3DB\Client($config, $connection);


// Here is some simple example code to connect and list records:

package main

import (
    "context"
    "fmt"
    "log"
    "os"

    "github.com/tozny/e3db-go"
)

func main() {
    client, err := e3db.GetDefaultClient()
    if err != nil {
        fmt.Fprint(os.Stderr, err)
        return
    }

    cursor := client.Query(context.Background(), e3db.Q{})
    for {
        record, err := cursor.Next()
        if err == e3db.Done {
            break
        } else if err != nil {
            log.Fatal(err)
        }
        fmt.Println(record.Meta.RecordID)
    }
}

Configuration is managed at runtime by instantiating an e3db.Config object with your client's credentials.

Write (Encrypt) New Data Record

  record = client.write('contact', {
    :first_name => 'Jon',
    :last_name => 'Snow',
    :phone => '555-555-1212'
  })
  printf("Wrote record %s\n", record.meta.record_id)
  Client client = ...; // Get a client instance

  Map<String, String> lyric = new HashMap<>();
  lyric.put("line", "Say I'm the only bee in your bonnet");
  lyric.put("song", "Birdhouse in Your Soul");
  lyric.put("artist", "They Might Be Giants");

  String recordType = "lyric";

  client.write(recordType, new RecordData(lyric), null, new ResultHandler<Record>() {
      @Override
      public void handle(Result<Record> r) {
        if(! r.isError()) {
          // record written successfully
          Record record = r.asValue();
          // Log or print record ID, e.g.:
          System.out.println("Record ID: " + record.meta().recordId());
        }
        else {
          // an error occurred
          throw new RuntimeException(r.asError().other());
        }
      }
    }
  );
// Create data for a record
var recordData map[string]string
recordType := "contact"
recordData["first_name"] = "Jon"
recordData["last_name"]  = "Snow"
recordData["phone"]      = "555-555-1212"

// Create optional metadata for the record
//(metadata can be used for searching)
var metadata map[string]string
matadata["realm"] = "The North"
metadata["pet"]   = "Ghost"

// Encrypt and save the record
recordID, err := client.Write(
  context.Background(), 
  recordType, 
  recordData, 
  metadata
  )
if err != nil {
    //Error handling omitted
}
fmt.Println("Wrote record: " + recordID)
// Wrap message in RecordData type to designate
// it as sensitive information for encryption
let recordData = RecordData(cleartext: ["SSN": "123-45-6789"])

// Can optionally include arbitrary metadata as `plain`
// where neither keys nor values are encrypted
e3db.write(type: "UserInfo", data: recordData, plain: ["Sent from": "my iPhone"]) { result in
    switch result {

        // The operation was successful, here's the record
        case .success(let record):

            // `record.meta` holds metadata associated
            // with the record, such as type.
            print("Wrote record! \(record.meta.recordId)")

        case .failure(let error):
            print("An error occurred attempting to write the data: \(error)")
        }
    }
}
import e3db

client = e3db.Client(
  # config
)

record_type = 'contact'
data = {
    'first_name': 'Jon',
    'last_name': 'Snow',
    'phone': '555-555-1212'
}

metadata = {
  'house' : 'Stark'
}

record = client.write(record_type, data, metadata)

print 'Wrote record {0}'.format(record.meta.record_id)

const e3db = require('e3db')

let client = new e3db.Client(/* config */)

async function main() {
  let data = {
    'first_name': 'Jon',
    'last_name': 'Snow',
    'phone': '555-555-1212',
  }
  let metadata = {
    'house' : 'Stark'
  }
  let record = await client.write('contact', data, metadata)

  console.log('Wrote record ' + record.meta.recordId)
}
main()

<?php
$data = [
    'name' => 'Jon Snow',
    'what_he_knows' => 'Nothing',
];
$metadata = [
    'house' => 'Stark'
];

// 'test-contact' represents our data type
$record = $client->write('test-contact', $data, $metadata);

//record contains the newly created value
$record_id = $record->meta->record_id;
?>

The above command returns a Record ID:


2c1b256e-8449-423a-955f-e2c508almnop

When a client creates data, its encrypted and signed locally and transmitted to E 3 DB for storage. Developers don’t have to manage the cryptography, only the form and structure of their data. Data can be queried using unencrypted metadata.

Parameters

Parameter Required Description
recordType true A string representing the type of record you are storing
data true An object of key value pairs representing data you want to store encrypted
metadata false An object of plain text meta about the data. Used to query

Read (Decrypt) Data Record

  let recordId = 'abc';
  let record = await client.read(recordId)
  new_record = client.read(record_id)
  print "Record: {0} {1}".format(new_record.data['Storage'], new_record.data['Unlock Code'])
  newRecord = client.read(record.meta.record_id)
  newRecord, err := client.Read(context.Background(), recordID)
  if err != nil {
    //Error handling omitted
  }
  fmt.Println (newRecord.Data["first_name"])
  e3db.read(recordId: recordId) { result in
    switch result {

    // The operation was successful, here's the record
    case .success(let record):

        // The record returned contains the same dictionary
        // supplied to the `RecordData` struct during the write
        print("Record data: \(record.data)")

    case .failure(let error):
        print("An error occurred attempting to read the record: \(error)")
    }
  }
<?php
$data = [
    'name' => 'Jon Snow',
    'what_he_knows' => 'Nothing',
];

// 'test-contact' represents our data type
$record = $client->read($recordId);

?>

The above command returns JSON structured like this:

{
  "data" : [
    "name" => "record_name",
  ],
  "meta" : [ 
    "your_meta" => "value"
  ]
}

Clients can read records by fetching records with their ID.

Clients can read data written by themselves or other clients (with permission). When reading data, it’s transmitted encrypted to the client and decrypted with the client’s private key, and its signature is verified.

Parameters

Parameter Required Description
recordId true The record you want to retrieve from TozStore

Update Data Record

  let recordId = 'abc'
  let record = await client.write(recordId)

  record.data.name = 'Mr. Jon Snow'
  let newRecord = await.client.update(record)
  original_record = client.read(record_id)
  original_record.data['name'] = 'Mr. Jon Snow'
  updated_record = client.update(original_record)
  print "Record: {0} {1}".format(updated_record.data['name'])
  record = client.read(recordId)
  record.data.name = "Mr. Jon Snow"
  newRecord = client.update(record)
  printf("Wrote record %s\n", newRecord.data.name)
  <?php
  $newData = [
      'name' => 'Jon Snow',
      'what_he_knows' => 'Something',
  ];

  // 'test-contact' represents our data type
  $record = $client->read($recordId);
  $record->data = $newData;
  $updatedRecord = $client->update($record);

  $updatedRecord->data['name'] = 'Mr. Jon Snow';
  $updatedRecord = $client->update($updatedRecord);

  ?>
  originalRecord, err := client.Read(context.Background(), recordID)
  if err != nil {
    //Error handling omitted
  }
  originalRecord.Data.name = "Mr. Jon Snow"
  newRecord, err := client.Update(context.Background(), originalRecord)
  if err != nil {
    //Error handling omitted
  }
  fmt.Println (newRecord.Data["name"])

The above command returns a record object:

  {
    "data" : [
      "name" => "record_name",
    ],
    "meta" : [ 
      "your_meta" => "value"
    ]
  }

The update function lets clients easily download, decrypt, verify, modify, encrypt, sign, and re-upload data in one step. Changes to data are versioned to prevent potential conflicts between multiple clients updating data simultaneously.

To update a record you must first download the record and update its properties as needed.
You then pass the updated Record object to the update function.

Parameter Required Description
record true The updated record object

Delete Data Record

  const e3db = require('e3db')

  let token = '...'
  let clientName = '...'
  let recordId = '...' //your record id to delete
  async function main() {
    await e3db.Client.delete(recordId)

    // ... Run operations with the client's details here
  }
  main()
  import e3db

  client = e3db.Client(
    # config
  )
  client.delete(record_id)
  # ---------------------------------------------------------
  # Initialization
  # ---------------------------------------------------------

  require 'e3db'

  # Configuration files live in ~/.tozny and you can have several
  # different "profiles" like *dev* and *production*.
  config = E3DB::Config.default

  # Now create a client using that configuration.
  client = E3DB::Client.new(config)
  client.delete(record_id)
  import com.tozny.e3db.*;
  String storedCredentials = ...; // Read from secure storage
  Client client = new ClientBuilder()
  .fromConfig(Config.fromJson(storedCredentials))
  .build();
  client.delete(recordId)
  <?php
  /**
  * Assuming your credentials are stored as defined constants in the
  * application, pass them each into the configuration constructor as
  * follows:
  */
  $config = new \Tozny\E3DB\Config(
    CLIENT_ID,
    API_KEY_ID,
    API_SECRET,
    PUBLIC_KEY,
    PRIVATE_KEY,
    API_URL
  );

  /**
  * Pass the configuration to the default coonection handler, which
  * uses Guzzle for requests. If you need a different library for
  * requests, subclass `\Tozny\E3DB\Connection` and pass an instance
  * of your custom implementation to the client instead.
  */
  $connection = new \Tozny\E3DB\Connection\GuzzleConnection($config);

  /**
  * Pass both the configuration and connection handler when building
  * a new client instance.
  */
  $client = new \Tozny\E3DB\Client($config, $connection);
  $client->delete('record_id');
  ?>
  err := client.Delete(recordId)
  if err != nil {
    //Error handling omitted
  }
  import E3db
  var e3db: //your client configuration
  e3db.delete(recordId: recordId)

This function simply deletes a record or set of records. Only a user with permission may delete data.

Parameter Required Description
record_id true The id of the record you want to delete

Query Data Records

  const e3db = require('e3db')

  let client = new e3db.Client(/* config */)

  let data = true
  let writer = null
  let record = null
  let type = 'contact'

  async function main() {
    let records = await client.query(data, writer, record, type).next()
    let fullName = record.data.first_name + ' ' + record.data.last_name
    console.log(fullName + ' --- ' + record.data.phone)
  }
  main()
  import e3db

  client = e3db.Client(' config ')

  record_type = 'contact'

  for record in client.query(record=[record_type]):
      full_name = "{0} --- {1}".format(record.data['first_name'], record.data['last_name'])
      print "{0} --- {1}".format(full_name, record.data['phone'])
  client.query(type: 'contact') do |record|
    fullname = record.data[:first_name] + ' ' + record.data[:last_name]
    printf("%-40s %s\n", fullname, record.data[:phone])
  end
  QueryParams params = new QueryParamsBuilder()
  .setTypes("lyric")
  .setIncludeData(true)
  .setCount(50)
  .build();

  Client client = ...; // Get a client instance

  client.query(params, new ResultHandler<QueryResponse>() {
    @Override
    public void handle(Result<QueryResponse> r) {
      if(! r.isError()) {
        // print list of records
        for(Record r : r.asValue().records()) {
          System.out.println("Record ID: " + r.meta().recordId());
          System.out.println("Song: " + r.data().get("song"));
        }
      }
    }
  }
  );
  $data = true;
  $raw = false;
  $writer = null;
  $record = null;
  $type = 'contact';

  $records = $client->query($data, $raw, $writer, $record, $type);
  foreach($records as $record) {
    $fullname = $record->data['first_name'] . ' ' . $record->data['last_name'];
    echo sprintf("%-40s %s\n", $fullname, $record->data['phone']);
  }
  // Keep track of queried batches
  var lastRead: Double?

  // Construct query, filtering to:
  // - return only 5 records at a time,
  // - only "UserInfo" type records,
  // - including records written by others
  //   that have been shared with this client
  let q1 = QueryParams(count: 5, types: ["UserInfo"], includeAllWriters: true)
  e3db.query(params: q1) { result in
      switch result {

      // The operation was successful, here's the `QueryResponse`,
      // which has the resulting records and an index for last record
      case .success(let resp):
          print("Records: \(resp.records)")
          lastRead = resp.last

      case .failure(let error):
          print("An error occurred attempting to query records: \(error)")
      }
  }

  // Query for next batch using `next`
  let q2 = q1.next(after: lastRead!)
  e3db.query(params: q2) { result in
      // ...
  }

Query E3DB records according to a set of selection criteria.

The default behavior is to return all records written by the current authenticated client.

To restrict the results to a particular type, pass a type or list of types as the type argument.

To restrict the results to a set of clients, pass a single or list of client IDs as the writer argument. To list records written by any client that has shared with the current client, pass the special string 'all' as the writer argument.

Parameter Required Type Description
data false Bool Flag to include data in records returned
writer false String or Array Records written by a single writer or list of writers
record false String or Array Select single record or list of records
type false String or Array Select records of a type or types
metadata false Array Associative array of plaintext meta to use as filter
pageSize false Number Page size returned by response

Share Data Records

  /**
 * ---------------------------------------------------------
 * Initialization
 * ---------------------------------------------------------
 */

  // Configuration values must be set in an immutable configuration object.
  // You can use whatever "profiles" or client credentials you want.
  let config = new e3db.Config(
    process.env.CLIENT_ID,
    process.env.API_KEY_ID,
    process.env.API_SECRET,
    process.env.PUBLIC_KEY,
    process.env.PRIVATE_KEY,
    process.env.API_URL,
    process.env.PUBLIC_SIGN_KEY,
    process.env.PRIVATE_SIGN_KEY
  )

  // Now create a client using that configuration
  let client = new e3db.Client(config)
  /**
  * ---------------------------------------------------------
  * Simple sharing by record type
  * ---------------------------------------------------------
  */

  // Share all of the records of type 'test-contact' with another client ID:
  let anotherClientId = 'db1sdfb9-3fb6-4458-a291-0bc673437dba08b'
  await client.share('test-contact', anotherClientId)
  # ---------------------------------------------------------
  # Initialization
  # ---------------------------------------------------------
  import e3db

  # Configuration files live in ~/.tozny and you can have several
  # different "profiles" like *dev* and *production*.

  # Load ~/.tozny/dev/e3db.json profile
  # conf = e3db.Config.load('dev')

  # Load default config in ~/.tozny/e3db.json
  conf = e3db.Config.load()

  # Now create a client using that configuration.
  client = e3db.Client(conf)
  # ---------------------------------------------------------
  # Simple sharing by record type
  # ---------------------------------------------------------

  # Share all of the records of type 'People' with another client ID:
  another_client_id = 'sd2329-3fb6-4458-a291-0bsdf23a08b'
  client.share('people', another_client_id)
  # ---------------------------------------------------------
  # Initialization
  # ---------------------------------------------------------

  require 'e3db'

  # Configuration files live in ~/.tozny and you can have several
  # different "profiles" like *dev* and *production*.
  config = E3DB::Config.default

  # Now create a client using that configuration.
  client = E3DB::Client.new(config)
  # ---------------------------------------------------------
  # Simple sharing by record type
  # ---------------------------------------------------------

  # Share all of the records of type 'test-contact' with another client ID:
  another_client_id = 'db1744b9-3fb6-4458-a291-sdf29008fj'
  client.share('test-contact', another_client_id)
  Client client = ...; // Get a client instance
  UUID readerId = ...; // Get the ID of the reader with whom we share

  client.share("lyric", readerId, new ResultHandler<Void>() {
    @Override
    public void handle(Result<Void> r) {
      if(! r.isError()) {
        // record shared
      }
    }
  });
  <?php
  // Configuration values must be set in an immutable configuration object.
  // You can use whatever "profiles" or client credentials you want.
  $config = new Config(
      \getenv('CLIENT_ID'),
      \getenv('API_KEY_ID'),
      \getenv('API_SECRET'),
      \getenv('PUBLIC_KEY'),
      \getenv('PRIVATE_KEY'),
      \getenv('API_URL')
  );
  // Now create a client using that configuration and a Guzzle-powered connection
  $connection = new GuzzleConnection($config);
  $client = new Client($config, $connection);
  /**
  * ---------------------------------------------------------
  * Simple sharing by record type
  * ---------------------------------------------------------
  */
  // Share all of the records of type 'test-contact' with another client ID:
  $another_client_id = 'db1744b9-3fb6-4458-a291-sdf2231';
  $client->share('test-contact', $another_client_id);
  ?>
  import (
    ...
    "github.com/tozny/e3db-go/v2"
    ...
  )
  // Share all "feedback" records with that user ID.
  client, err := e3db.GetDefaultClient()
  chk(err)
  err = client.Share(context.Background(), "feedback", feedbackClient.ClientID)
  chk(err)
  // Get the recipient client ID externally
  let otherClient: UUID = ???

  // Share records of type "UserInfo" with another client
  e3db.share(type: "UserInfo", readerId: otherClient) { result in
      guard case .success = result else {
          return print("An error occurred attempting to grant access to records: \(result.error)")
      }
      // Sharing was successful!
  }

These functions change access control and add or remove an encryption key so that a new party can read or write data. It can be run by any client with permission to this data. It does not require downloading or re-encrypting the data, since TozStore utilizes a key wrapping approach for efficiency. As a result, a very large amount of data can be shared with multiple parties using a fast, constant-time operation.

Parameter Required Type Description
record_type true String The 'type' of records that should be shared with a client
client_id true String The recipient who will get access to the records

Unshare Data Records

  /**
 * ---------------------------------------------------------
 * Initialization
 * ---------------------------------------------------------
 */

  // Configuration values must be set in an immutable configuration object.
  // You can use whatever "profiles" or client credentials you want.
  let config = new e3db.Config(
    process.env.CLIENT_ID,
    process.env.API_KEY_ID,
    process.env.API_SECRET,
    process.env.PUBLIC_KEY,
    process.env.PRIVATE_KEY,
    process.env.API_URL,
    process.env.PUBLIC_SIGN_KEY,
    process.env.PRIVATE_SIGN_KEY
  )

  // Now create a client using that configuration
  let client = new e3db.Client(config)
  /**
  * ---------------------------------------------------------
  * Simple sharing by record type
  * ---------------------------------------------------------
  */

  // Revoke all of the records of type 'test-contact' with another client ID:
  let anotherClientId = 'db1sdfb9-3fb6-4458-a291-0bc673437dba08b'
  await client.revoke('test-contact', anotherClientId)
  # ---------------------------------------------------------
  # Initialization
  # ---------------------------------------------------------
  import e3db

  # Configuration files live in ~/.tozny and you can have several
  # different "profiles" like *dev* and *production*.

  # Load ~/.tozny/dev/e3db.json profile
  # conf = e3db.Config.load('dev')

  # Load default config in ~/.tozny/e3db.json
  conf = e3db.Config.load()

  # Now create a client using that configuration.
  client = e3db.Client(conf)
  # ---------------------------------------------------------
  # Simple sharing by record type
  # ---------------------------------------------------------

  # Revoke all of the records of type 'People' with another client ID:
  another_client_id = 'sd2329-3fb6-4458-a291-0bsdf23a08b'
  client.revoke('people', another_client_id)
  # ---------------------------------------------------------
  # Initialization
  # ---------------------------------------------------------

  require 'e3db'

  # Configuration files live in ~/.tozny and you can have several
  # different "profiles" like *dev* and *production*.
  config = E3DB::Config.default

  # Now create a client using that configuration.
  client = E3DB::Client.new(config)
  # ---------------------------------------------------------
  # Simple sharing by record type
  # ---------------------------------------------------------

  # Revoke all of the records of type 'test-contact' with another client ID:
  another_client_id = 'db1744b9-3fb6-4458-a291-sdf29008fj'
  client.revoke('test-contact', another_client_id)
  Client client = ...; // Get a client instance
  UUID readerId = ...; // Get the ID of the reader with whom we share

  client.revoke("lyric", readerId, new ResultHandler<Void>() {
    @Override
    public void handle(Result<Void> r) {
      if(! r.isError()) {
        // record shared
      }
    }
  });
  <?php
  // Configuration values must be set in an immutable configuration object.
  // You can use whatever "profiles" or client credentials you want.
  $config = new Config(
      \getenv('CLIENT_ID'),
      \getenv('API_KEY_ID'),
      \getenv('API_SECRET'),
      \getenv('PUBLIC_KEY'),
      \getenv('PRIVATE_KEY'),
      \getenv('API_URL')
  );
  // Now create a client using that configuration and a Guzzle-powered connection
  $connection = new GuzzleConnection($config);
  $client = new Client($config, $connection);
  /**
  * ---------------------------------------------------------
  * Simple sharing by record type
  * ---------------------------------------------------------
  */
  // Revoke all of the records of type 'test-contact' with another client ID:
  $another_client_id = 'db1744b9-3fb6-4458-a291-sdf2231';
  $client->revoke('test-contact', $another_client_id);
  ?>
  import (
    ...
    "github.com/tozny/e3db-go/v2"
    ...
  )
  // Share all "feedback" records with that user ID.
  client, err := e3db.GetDefaultClient()
  chk(err)
  err = client.Share(context.Background(), "feedback", feedbackClient.ClientID)
  chk(err)
  // Get the recipient client ID externally
  let otherClient: UUID = ???

  // Remove access to "UserInfo" records from the given client
  e3db.revoke(type: "UserInfo", readerId: otherClient) { result in
      guard case .success = result else {
          return print("An error occurred attempting to revoke access to records: \(result.error)")
      }
      // Revoking was successful!
  }

These functions change access control and add or remove an encryption key so that a new party can read or write data. It can be run by any client with permission to this data. It does not require downloading or re-encrypting the data, since TozStore utilizes a key wrapping approach for efficiency. As a result, a very large amount of data can be unshared with multiple parties using a fast, constant-time operation.

Parameter Required Type Description
record_type true String The 'type' of records that should be shared with a client
client_id true String The recipient who will get access to the records

Upload File

  # ---------------------------------------------------------
  # Initialization
  # ---------------------------------------------------------
  import e3db

  # Configuration files live in ~/.tozny and you can have several
  # different "profiles" like *dev* and *production*.

  # Load ~/.tozny/dev/e3db.json profile
  # conf = e3db.Config.load('dev')

  # Load default config in ~/.tozny/e3db.json
  conf = e3db.Config.load()

  # Now create a client using that configuration.
  client = e3db.Client(conf)

  record_type = "test_large_files"

  # Plaintext file that we want to encrypt and send to E3DB for storage
  plaintext_filename = "mylargefile.txt"

  # Encrypt and write the file to the E3DB Server
  encrypted_file_meta = client.write_file(record_type, plaintext_filename)
  ...
  File readmeFile = new File("README.md");
  String recordType = "docs";

  client.writeFile(recordType, readmeFile, null, new ResultHandler<RecordMeta>() {
      @Override
      public void handle(Result<RecordMeta> r) {
        if(! r.isError()) {
          // file written successfully
          RecordMeta record = r.asValue();
          // Log or print record ID, e.g.:
          System.out.println("Record ID: " + record.meta().recordId());
        }
        else {
          // an error occurred
          throw new RuntimeException(r.asError().other());
        }
      }
    }
  );
  let data = Data("Hello there".utf8)
  let src  = FileManager
      .default
      .temporaryDirectory
      .appendingPathComponent("source-file")
      .appendingPathExtension("txt")

  guard FileManager.default.createFile(atPath: src.path, contents: data) else {
      return print("Could not create file")
  }

  var recordId: UUID?
  e3db.writeFile(type: type, fileUrl: src) { result in
      switch result {
      // The operation was successful, here's the `Meta` instance.
      case .success(let meta):
          recordId = meta.recordId
      case .failure(let error):
          print("An error occurred attempting to write file: \(error)")
      }
  }

TozStore supports the storage of large encrypted files, using a similar interface for reading and writing records. The SDK will handle encrypting and uploading the file. Similarly, it will download and decrypt files as well. Note that file upload and download is currently in development and not supported in all of our SDK's. If you are actively looking for support in a specific library please contact us at support@tozny.com.

Download File

  # ---------------------------------------------------------
  # Initialization
  # ---------------------------------------------------------
  import e3db

  # Configuration files live in ~/.tozny and you can have several
  # different "profiles" like *dev* and *production*.

  # Load ~/.tozny/dev/e3db.json profile
  # conf = e3db.Config.load('dev')

  # Load default config in ~/.tozny/e3db.json
  conf = e3db.Config.load()

  # Now create a client using that configuration.
  client = e3db.Client(conf)

  record_type = "test_large_files"

  # Plaintext file that we want to encrypt and send to E3DB for storage
  plaintext_filename = "mylargefile.txt"

  # Encrypt and write the file to the E3DB Server
  read_file_info = client.read_file(encrypted_file_meta.record_id, plaintext_filename)
  ...
  UUID readmeRecordId = ...; // Record ID of file written previously
  File destinationFile = new File("README-2.md");

  client.readFile(readmeRecordId, destinationFile, new ResultHandler<RecordMeta>() {
      @Override
      public void handle(Result<RecordMeta> r) {
        if(r.isError()) {
          // an error occurred
          throw new RuntimeException(r.asError().other());
        }

        // `README-2.md` will contain contents of file, which we write to standard out.
        try {
          System.out.println("README-2.md: " + new String(Files.readAllBytes(destinationFile), "UTF-8"));
        }
        catch (IOException e) {
        // Handle error where file isn't found ...
        }
      }
    }
  );
  let dest = FileManager
    .default
    .temporaryDirectory
    .appendingPathComponent("destination-file")
    .appendingPathExtension("txt")

  guard FileManager.default.createFile(atPath: dest.path, contents: nil) else {
      return print("Could not create file")
  }

  e3db.readFile(recordId: recordId, destination: dest) { result in
      switch result {
      case .success:
          print("File was downloaded, decrypted, and saved to \(dest.path)!")
      case .failure(let error):
          print("An error occurred attempting to read file: \(error)")
      }
  }

TozStore supports the storage of large encrypted files, using a similar interface for reading and writing records. The SDK will handle encrypting and uploading the file. Similarly, it will download and decrypt files as well. Note that file upload and download is currently in development and not supported in all of our SDK's. If you are actively looking for support in a specific library please contact us at support@tozny.com.

Authorize User (Beta)

Share on behalf of (Beta)

Errors

The Kittn API uses the following error codes:

Error Code Meaning
400 Bad Request -- Your request is invalid.
401 Unauthorized -- Your API key is wrong.
403 Forbidden -- The kitten requested is hidden for administrators only.
404 Not Found -- The specified kitten could not be found.
405 Method Not Allowed -- You tried to access a kitten with an invalid method.
406 Not Acceptable -- You requested a format that isn't json.
410 Gone -- The kitten requested has been removed from our servers.
418 I'm a teapot.
429 Too Many Requests -- You're requesting too many kittens! Slow down!
500 Internal Server Error -- We had a problem with our server. Try again later.
503 Service Unavailable -- We're temporarily offline for maintenance. Please try again later.