sylo logo circle
Sylo logo

DOCS

Deprecated!

This version of the Sylo Protocol is deprecated, however, the new version will be available soon.

Sylo Protocol is a toolkit for building decentralised messaging applications atop the Sylo Network.

An example use case is the Sylo Smart Wallet.

Sylo Protocol does not rely on the operation of any centralized architecture in order to function and function well. All operations are carried out in a peer to peer setting. As elaborated upon in the next section, this gives rise to a number of desirable security- and confidentiality- impacting properties.

Key values

Secure and confidential

Sylo Protocol provides a high level of security and confidentiality. Since everything happens in a peer to peer setting, state and state transitions are propagated autonomously through the network. Aside from removing the intermediaries, however, Sylo Protocol also goes to great length to protect communications and profiles in the unlikely event that a single message or session key gets compromised by responsibly securing all communications using Signal Protocol.

Performant

In order to be adapted, decentralized solutions need to provide an experience that is on-par or even better than traditional communication platforms. Given that in a decentralized setting operations fall onto the clients themselves, we look at performance as a first-class feature, not an after-thought.

This includes:

  • Reasonably low bandwidth consumption
  • Reasonably low memory consumption
  • Reasonably low CPU consumption

Highly fault-tolerant

By the very nature of how software operates and integrates, failure is inevitable. In fact, shifting business logic from central servers onto clients themselves only pronounces the need for robust and responsible fault management, where recovering from disaster is not the responsibility of a dedicated operations team, but that of the very application itself.

Sylo Protocol thus considers fault-tolerance and recovery to be a first-class feature that is deeply ingrained in all technical decisions the Sylo Protocol core team makes.

Feature-rich

Wide adoption of any new system - decentralized or not - is only possible if the experience is on-par or better than what is currently available and adopted by the target audience. Hence, Sylo Protocol aims to provide a feature set that is capable of matching user expectations, while simultaneously solving for the aforementioned goals.

Resilient to bad network connections

In the spirit of being generally fault-tolerant, Sylo Protocol does not assume a speedy network connection - or any network connection at all for that matter - in order to operate. State changing operations are scheduled for eventual delivery if network conditions are poor or no networks are available at the time the state transition is scheduled.

Features

Offline-first

Work entirely offline when network connectivity cannot be established or peers are temporarily unavailable. State changing operations are always queued for eventual delivery and propagation.

Profiles

Manage your Sylo profile. Your profile serves as a simple key/value store of application-defined user information that is optionally made available to all Sylo peers or a select subset thereof. Profiles enjoy the same level protection as any other feature built-into Sylo Protocol by being blessed with end to end encryption and forward secrecy.

Contacts

Manage your Sylo contacts. Contacts are “self-vetted” peers on the Sylo network a user may choose to connect and stay up-to-date with. Contact relationships are, technically speaking, always unidirectional. In practice, however, contact relationships are often reciprocated by the contact themselves.

Groups

Manage your Sylo groups. Groups are a simple yet powerful construct that gives rise to a number of other features such as group chat and group calls. Groups are effectively a collection of participating members, where each member is simply a Sylo identity coupled with a set of assigned roles.

Invitations

Receive and accept or reject invitations to a group. The invitation envelope undergoes the same security considerations as everything else in Sylo Protocol. Only the recipient and current group members will know about the invitation, and involvement with the group remains fully deniable up until the point of accepting and interacting with the group.

Roles and permissions

Manage group membership by assigning or removing roles to/from members, thereby granting or restricting members to the actions they may legitimately take in the context of the group. Roles may be re-assigned or dropped over time by sufficiently privileged members in the group. Since the group’s state is immutable and ever-evolving, previous decisions can be audited, challenged and even undone in the event that a sufficiently privileged member is deemed to have misused their role.

Multiple devices

Manage multiple devices under a single Sylo identity. Devices attempt to stay in sync with each other on a best-effort basis.

List devices

Sylo Protocol keeps a tap on your currently active devices which can be readily inspected: when and where was a device last used? when was it last synced? Needless to say, all of this information is private to the owning user and not stored on any servers.

Add new devices

Add a new device by pairing with an existing device or by importing your Sylo identity via other means.

Remove devices

Decomission unused devices by no longer advertising them to peers. Peers will refrain from sharing information with the given device from the moment they learn about it being decomissioned.

Sync devices

All known devices are automatically synced with each other. All device to device communications undergo the same security treatments as the rest of the Sylo Protocol suite.

Getting Started

Sylo Protocol runs as a daemon and exposes a protobuf-based RPC interface to interact with. The daemon can be run in a variety of ways and configurations. This document focuses on the standalone node.js daemon for simplicity.

Installing the Sylo Protocol daemon

Requirements

  • Golang toolchain
  • Clang toolchain
  • Build essentials
  • TODO install in a docker-container to confirm
  • TODO check OS compat (Windows, anyone?)
npm i -g @sylo/protocol-v2

Starting the Sylo Protocol daemon

# binds a UNIX domain socket 'sylo.sock' in CWD.
# Use the '--socket' option to alter this path.
sylo-protocol daemon

Talking to the Sylo Protocol daemon

The Sylo Protocol daemon speaks a custom RPC protocol that is centered around requests, events, errors, and responses. The raw RPC API is detailed here.

note — In the context of JavaScript/TypeScript, we recommend using the official, full-featured, and type-safe client to get started with.

Additionally, a fully self-documenting CLI is available as well. For example:

sylo-protocol identity-generate
{
  "identity": {
    "syloId": {
      "syloId": {
        "publicKey": "SnL92n6F/+o2HTI41hSG7YixkJ1jzHJn0dzZ5UyEzNY="
      }
    },
    "syloKey": {
      "publicKey": {
        "publicKey": "SnL92n6F/+o2HTI41hSG7YixkJ1jzHJn0dzZ5UyEzNY="
      },
      "secretKey": {
        "secretKey": "kh29nNZ2ZMxApYBMvKzl3HpzynpMdEV5+loLpDhqyfFKcv3afoX/6jYdMjjWFIbtiLGQnWPMcmfR3NnlTITM1g=="
      }
    }
  },
  "device": {
    "deviceId": {
      "deviceId": {
        "publicKey": "ClP2IvoRBvoYaL+uetCDJbidfFFI3tj+48erow3gKxk="
      }
    },
    "hostKey": {
      "publicKey": {
        "publicKey": "ClP2IvoRBvoYaL+uetCDJbidfFFI3tj+48erow3gKxk="
      },
      "secretKey": {
        "secretKey": "RTEkeLF2womZcmtvE2KamIS97ICa1MM+jzTxL3LcvskKU/Yi+hEG+hhov6560IMluJ18UUje2P7jx6ujDeArGQ=="
      }
    },
    "signalIdentity": {
      "publicKey": "7RmXdtanltygt4GCi5dSuRizB0lSlSJ+Pt2gkcaWIJo="
    }
  }
}

To view all available commands and their options, use sylo-protocol --help.

The Sylo-Protocol shell (Linux, MacOS only)

The “@sylo/protocol-v2” npm package further comes with a handy shell to explore the API and write scripts in.

sylo-protocol-shell

All sylo-commands are available as top-level commands. For example:

note For purposes of being more discoverable, all sylo-commands are further aliased with an ’@’ prefix. For example: “@identity-generate”.

Selecting a user/device pair

The shell enables setting the current user/device pair in order to ease working with a specific account. Any commands that operate in the context of an account will source this information if available.

Example command-line usage

The following program demonstrates a complete flow of two users creating a sylo id, creating a group, creating, exchanging, and accepting invitations to join the group, as well as sending and receiving messages.

#!/bin/bash
set -eo pipefail

# usage: to_sylo_id <account>
function to_sylo_id () { jq -r <<< "$1" '.identity.syloId.value.publicKey'; }

# usage: to_device_id <account>
function to_device_id () { jq -r <<< "$1" '.device.deviceId.value.publicKey'; }

# usage: to_group_id <group>
function to_group_id () { jq -r <<< "$1" '.groupId.value'; }

# create a fresh account for alice
alice=$(sylo-protocol identity-generate)
alice_sylo_id=$(to_sylo_id "$alice")
alice_device_id=$(to_device_id "$alice")

# create a fresh account for bob
bob=$(sylo-protocol identity-generate)
bob_sylo_id=$(to_sylo_id "$bob")
bob_device_id=$(to_device_id "$bob")

# add contact
sylo-protocol contact-add \
    --user "$alice_sylo_id" \
    --device "$alice_device_id" \
    "$bob_sylo_id"

# create a new group with contact
group=$(sylo-protocol groups-create \
    --user "$alice_sylo_id" \
    --device "$alice_device_id")
group_id="$(to_group_id "$group")"

# alice start syncing in background
sylo-protocol groups-run-sync-process \
    --user "$alice_sylo_id" \
    --device "$alice_device_id" \
    --group-id "$group_id" &
alice_sync_pid=$!

# invite bob to the group
invitations=$(sylo-protocol groups-create-invitations \
    --user "$alice_sylo_id" \
    --device "$alice_device_id" \
    --group-id "$group_id" \
    --to "$bob_sylo_id")

# fish out the invitation for bob
invitation=$(jq -r <<< "$invitations" '.invitations[0]')

# ...
# ... the invitation travels to bob out of band
# ...

# now bob can accept the invite
sylo-protocol groups-accept-invitation \
    --user "$bob_sylo_id" \
    --device "$bob_device_id" \
    --invitation "$invitation"

# bob start syncing in background
sylo-protocol groups-run-sync-process \
    --user "$bob_sylo_id" \
    --device "$bob_device_id" \
    --group-id "$group_id" &
bob_sync_pid=$!

# ...
# ... alice learns that bob accepted the invitation
# ...

# alice sends a plain-text message to bob
sylo-protocol groups-groups-post \
    --user "$alice_sylo_id" \
    --device "$alice_device_id" \
    --encryption PLAIN \
    --post "hello!"

# ...
# ... bob learns about the new post because of he's running
# ... 'groups-run-sync-process'
# ...

sylo-protocol groups-query-posts \
    --user "$bob_sylo_id" \
    --device "$bob_device_id" \
    --group-id "$group_id"

API Reference

Identity API

IdentityGenerate

Generate a new Sylo identity key and initialize a new Sylo device.

Parameters

This command does not take any parameters

Errors

A failed invocation may result in one of the following error conditions

Error Description
.initializeError Device initialization failed

Returns

A successful invocation will yield the following result

Field Description
.identity The generated sylo identity
.device The generated device

IdentityImport

Derive an existing Sylo identity key from the given seed and initialize a new Sylo device.

Parameters

Name Description
.seed Seed value in hexadecimal

Errors

A failed invocation may result in one of the following error conditions

Error Description
.initializeError Device initialization failed
.identityAlreadyExists The given identity already exists

Returns

A successful invocation will yield the following result

Field Description
.identity The generated sylo identity
.device The generated device

IdentityList

List all known sylo-ids and their device(s).

Parameters

This command does not take any parameters

Returns

A successful invocation will yield the following result

Field Description
.identities all known sylo-ids and their devices

Contact API

ContactList

List all contacts of an account.

Parameters

Name Description
.accountId The account to operate on

Errors

A failed invocation may result in one of the following error conditions

Error Description
.noSuchAccount The given account does not exist

Returns

A successful invocation will yield the following result

Field Description
.contacts The list of contacts

ContactAdd

Add a given sylo-id as a contact to an account.

Parameters

Name Description
.accountId The account to operate on
.contact The sylo id of the contact to add
.meta Extraneous meta information to associate with the contact

Errors

A failed invocation may result in one of the following error conditions

Error Description
.noSuchAccount The given account does not exist
.contactAlreadyExists The contact already exists
.cannotAddSelf Adding yourself as a contact is not possible
.openError TODO
.appendError TODO

Returns

A successful invocation will yield the following result

Field Description
.contact The created contact

ContactRemove

Remove the given sylo identifiers from the session owner’s contact book.

Parameters

Name Description
.accountId The account to operate on
.contact The contact to remove

Errors

A failed invocation may result in one of the following error conditions

Error Description
.noSuchAccount The given account does not exist
.contactDoesNotExist The given contact does not exist
.openError TODO
.appendError TODO

Returns

This command returns nothing (side-effects only)

ContactGet

Retrieve a contact

Parameters

Name Description
.accountId The account to operate on
.contact The sylo-id of the contact to retrieve

Errors

A failed invocation may result in one of the following error conditions

Error Description
.noSuchAccount The given account does not exist

Returns

A successful invocation will yield the following result

Field Description
.contact The retrieved contact

ContactEdit

Edit meta information associated with a given contact

Parameters

Name Description
.accountId The account to operate on
.contact The sylo-id of the contact to operate on
.meta The updated meta

Errors

A failed invocation may result in one of the following error conditions

Error Description
.noSuchAccount The given account does not exist
.contactDoesNotExist The given contact does not exist
.openError TODO
.appendError TODO

Returns

This command returns nothing (side-effects only)

ContactCreateRequest

Create a contact request for the given sylo-id.

Parameters

Name Description
.accountId The account to operate on
.contact The sylo-id of the contact to create a contact request for
.meta Any extraneous meta information to associate with this contact request.

Errors

A failed invocation may result in one of the following error conditions

Error Description
.noSuchAccount The given account does not exist
.noDeviceForAccount The given account does not have an associated device
.signalNotInitialized N/A** to be removed
.cannotAddSelf Creating a contact request for one-self is not allowed

Returns

A successful invocation will yield the following result

Field Description
.envelope The contact-request envelope

Profile API

ProfilePublicGet

Retrieve your own public profile

Parameters

Name Description
.accountId The account to operate on

Errors

A failed invocation may result in one of the following error conditions

Error Description
.noSuchAccount The given account does not exist

Returns

A successful invocation will yield the following result

Field Description
.meta The retrieved profile’s associated meta information

ProfilePublicUpdate

Update your own public profile

Parameters

Name Description
.accountId The account to operate on
.meta The updated meta information to associate with the profile.
This information is merged into any existing information on
the profile.

Errors

A failed invocation may result in one of the following error conditions

Error Description
.noSuchAccount The given account does not exist
.openError TODO
.appendError TODO

Returns

This command returns nothing (side-effects only)

ProfilePrivateGet

Retrieve your own private profile

Parameters

Name Description
.accountId The account to operate on

Errors

A failed invocation may result in one of the following error conditions

Error Description
.noSuchAccount The given account does not exist

Returns

A successful invocation will yield the following result

Field Description
.meta The retrieved profile’s associated meta information

ProfilePrivateUpdate

Update your own private profile

Parameters

Name Description
.accountId The account to operate on
.meta The updated meta information to associate with the profile.
This information is merged into any existing information on
the profile.

Errors

A failed invocation may result in one of the following error conditions

Error Description
.noSuchAccount The given account does not exist
.openError TODO
.appendError TODO

Returns

This command returns nothing (side-effects only)

Groups API

GroupsCreate

Create a new group.

Parameters

Name Description
.accountId The account to operate on
.meta Extraneous meta information to associate with the group.
.configuration The group configuration to initialize the group with.

Errors

A failed invocation may result in one of the following error conditions

Error Description
.noSuchAccount The given account does not exist
.openError TODO
.appendError TODO

Returns

A successful invocation will yield the following result

Field Description
.groupId The generated/assigned group-id

GroupsView

Retrieve the current state of a group.

Parameters

Name Description
.accountId The account to operate on
.groupId The group to query the current state of.

Errors

A failed invocation may result in one of the following error conditions

Error Description
.noSuchAccount The given account does not exist

Returns

A successful invocation will yield the following result

Field Description
.group The current group state

GroupsList

List all groups the given, managed account-id is a part of.

Parameters

Name Description
.accountId The account to operate on

Errors

A failed invocation may result in one of the following error conditions

Error Description
.noSuchAccount The given account does not exist

Returns

A successful invocation will yield the following result

Field Description
.groups The group-ids of all groups the given account-id is a part of

GroupsCreateInvitations

Create an invitation to a group.

Parameters

Name Description
.accountId The account to operate on
.groupId The group to create an invitation for
.invitees Who to create an invitation for, their roles, and associated
meta information.

Errors

A failed invocation may result in one of the following error conditions

Error Description
.noSuchAccount The given account does not exist
.openError TODO
.appendError TODO
.unknownGroup The given group-id is unknown to the operating, managed sylo-id
.nonMember The operating sylo-id is not in the group
.memberAlreadyExists The invited sylo-id is already part of the group
.insufficientPrivileges The operating sylo-id did not have sufficient privileges to
create an invitation to the group

Returns

A successful invocation will yield the following result

Field Description
.invitations The list of created invitations

GroupsReceiveInvitation

Receive an invitation to a group

Parameters

Name Description
.accountId The account to operate on
.invitation The invitation to receive

Errors

A failed invocation may result in one of the following error conditions

Error Description
.noSuchAccount The given account does not exist
.openError TODO
.appendError TODO

Returns

This command returns nothing (side-effects only)

GroupsRevokeInvitation

Revoke a previously issued invitation to a group

Parameters

Name Description
.accountId The account to operate on
.groupId The id of the group the previously issued invitation was for
.invitationId The id of the previously issued invitation

Errors

A failed invocation may result in one of the following error conditions

Error Description
.noSuchAccount The given account does not exist
.openError TODO
.appendError TODO

Returns

This command returns nothing (side-effects only)

GroupsRejectInvitation

Reject a previously received invitation to a group.

Parameters

Name Description
.accountId The account to operate on
.invitationId The id of the previously received invitation

Errors

A failed invocation may result in one of the following error conditions

Error Description
.noSuchAccount The given account does not exist

Returns

This command returns nothing (side-effects only)

GroupsListReceivedInvitations

List all received invitations to groups.

Parameters

Name Description
.accountId The account to operate on

Errors

A failed invocation may result in one of the following error conditions

Error Description
.noSuchAccount The given account does not exist

Returns

A successful invocation will yield the following result

Field Description
.invitations The list of all received invitations

GroupsAcceptInvitation

Accept a previously received invitation to a group.

Parameters

Name Description
.accountId The account to operate on
.invitation The previously received invitation

Errors

A failed invocation may result in one of the following error conditions

Error Description
.noSuchAccount The given account does not exist
.openError TODO
.appendError TODO

Returns

This command returns nothing (side-effects only)

GroupsRemoveMember

Remove a member from a group

Parameters

Name Description
.accountId The account to operate on
.groupId The id of the group the given member is to be removed from.
.member The sylo-id of the member to be removed

Errors

A failed invocation may result in one of the following error conditions

Error Description
.noSuchAccount The given account does not exist
.openError TODO
.appendError TODO

Returns

This command returns nothing (side-effects only)

GroupsDefineRole

Define a new role for the given group to use.

Parameters

Name Description
.accountId The account to operate on
.groupId The id of the group to define the role in
.role The newly defined role

Errors

A failed invocation may result in one of the following error conditions

Error Description
.noSuchAccount The given account does not exist
.openError TODO
.appendError TODO

Returns

This command returns nothing (side-effects only)

GroupsGrantRole

Assign a given member of a given group to a given role.

Parameters

Name Description
.accountId The account to operate on
.groupId The id of the group to operate on
.member The sylo-id of the member to assign to the given role
.role The role to assign to the given member

Errors

A failed invocation may result in one of the following error conditions

Error Description
.noSuchAccount The given account does not exist
.openError TODO
.appendError TODO

Returns

This command returns nothing (side-effects only)

GroupsRevokeRole

Remove a given member of a given group from a given role.

Parameters

Name Description
.accountId The account to operate on
.groupId The id of the group to operate on
.member The sylo-id of the member to remove the given role from
.role The role to remove from the given member

Errors

A failed invocation may result in one of the following error conditions

Error Description
.noSuchAccount The given account does not exist
.openError TODO
.appendError TODO

Returns

This command returns nothing (side-effects only)

GroupsPost

Create a new post in a given group.

Parameters

Name Description
.accountId The account to operate on
.groupId The id of the group to create a new post in
.tag An string tag to assign to the post
.content Arbitrary post content
.meta Extraneous information to associate with the post
.encryption The encryption mechanism used

Errors

A failed invocation may result in one of the following error conditions

Error Description
.noSuchAccount The given account does not exist
.openError TODO
.appendError TODO

Returns

This command returns nothing (side-effects only)

GroupsQueryPosts

Query posts in a given group.

Parameters

Name Description
.accountId The account to operate on
.groupId The id of the group to query posts in
.meta Meta information to filter posts by
.offset An optional offset into query
.limit An optional limit of query results
.ordering The order in which the results are retrieved

Errors

A failed invocation may result in one of the following error conditions

Error Description
.noSuchAccount The given account does not exist

Returns

A successful invocation will yield the following result

Field Description
.posts The list of matching posts.

GroupsRunSyncProcess

Sync a given group. This is a non-terminating command - the group will be synced interactively for the duration up until the point of cancellation.

Parameters

Name Description
.accountId The account to operate on
.groupId The id of the group to sync

Errors

A failed invocation may result in one of the following error conditions

Error Description
.noSuchAccount The given account does not exist

Events

These events are produced over the lifetime of the api call

Event Description
.newInvitations New invitations have become available
.newMember A new member has joined the group
.newPost A new post has been received

Returns

This command returns nothing (side-effects only)

Official TypeScript client

In the context of JavaScript/TypeScript applications, we recommend the official TypeScript client as it fully supports every aspect the RPC interface has to offer, from extensive error handling, over to event handling, and dealing with multiple transport implementations.

Installation

The official TypeScript client is available on npm, and can be installed via the following command line:

npm i @sylo/protocol-v2-client

Example usage

The following code snippet demonstrates how to use the client over a UNIX domain socket.

import { Client, UnixTransport } from '@sylo/protocol-client-v2'
async function main() {
  const transport = await UnixTransport.create('./sylo.sock' /* path to UNIX socket */)
  const client = new Client(transport) /* takes ownership */
  const result = await client.identityGenerate()
  // ...
}
main()

Errors and Responses

All API functions eventually yield an IResult, which is either an Err or an Ok instance, forcing a check for errors prior to using any return value.

export interface IResult<E, T> {
  map<Z>(f: (t: T) => Z): IResult<E, Z>
  mapErr<Z>(f: (e: E) => Z): IResult<Z, T>
  unwrap(): T | never
}

Each API call associates a type for successful and one for errorneous responses. Additionally, all errorneous responses are further encapsulated in errors that can always occur, such as transport-related or encoding related errors.

type SomeError<TE, E> = TransportError<TE> | Cancelled | InternalError | MalformedMessage | MalformedResponse | CommandError<E>

In summary, all commands yield values of the following type:

type CommandResult<E, O> = CancellablePromise<IResult<SomeError<TransportError, E>, O>>

Example

An example of safely dealing with results:

import SPC from '@sylo/protocol-v2-client';

const client = /* ... get a client somehow ... */;
const result = await client.IdentityGenerate();

if (result instanceof Err) {
    if (result.error instanceof Cancelled) {
        /* Command was cancelled. What now? ... */
    } else if ... {
    } else if (result.error instanceof CommandError)
        const error = result.error.error;
        if (error.initializeError) {
            /* Device initialization failed. What now? ... */
        }
    } else {
        never;
    }
} else {
    /* The happy path */
    console.log(`Generated account ${result.value}`)
}

Events

Some commands are intended to run over an extended period of time or to never terminate. These commands communicate with the caller by emitting events. API calls that feature events take an additional event handler callback.

Cancellation

All API functions return a CancellablePromise - A promise that is blessed with the ability to be cancelled:

const pendingResult = client.identityGenerate()
pendingResult.cancel()

Cancelling an API call releases any allocated resources, including processes, the daemon may have acquired or started in response to the API call.

note

CancellablePromise implement the Promise interface and are thus fully compatible with the async/await syntax.

Sylo Protocol Identity

Identity on the Sylo network is synonymous with the Sylo Identity Key which is a public/private key-pair derived from a mnemonic seed phrase during setup.

The Sylo IK is the single most important piece of information a user must safeguard from both being lost and being compromised. A lost Sylo IK is irrecoverable, while a compromised Sylo IK is irrevocable.

It is advised to store the mnemonic seed phrase underpinning the Sylo IK on an alternative medium altogether, as it might be prompted for when configuring an additional device. Sylo Protocol leaves this as an application-level problem to solve, however.

IMPORTANT

It is the task of the application-layer to communicate the importance of safely storing and transmitting the Sylo IK in a way accessible to the average user.

The derived Sylo IK is to never leave the user’s device under normal operation, is to be symmetrically encrypted at rest, and stored using the target platform’s most appropriate means of doing so.

The Sylo Identity Key

The Sylo IK employs a Edwards-curve Digital Signature Algorithm (EdDSA) scheme with ed25519 parameters. Ed25519 has the following attractive properties:

High performance

The selected scheme performs efficiently on a variety of platforms and features small public key (32 bytes) and signature (64 bytes) sizes. This makes the scheme suitable to work on mobile and similarly constrained platforms.

Asymmetric encryption

Keypairs generated for ed25519 can conveniently be converted for use with X25519, which is a Elliptical Curve Diffie-Helmin scheme that Sylo employs for asymmetric encryption.

Devices

Users may operate one or more devices. Ownership over a device is claimed via entries maintained in the user’s profile log.

Operating a device provides the user with the following capabilities:

Connect to peers on the Sylo network A device maps onto a libp2p peer-id via its Host IK which is generated randomly during device setup. Communication via the libp2p-provided networking primitives happens exclusively in terms of these host keys.

** End to end encryption** A device maps onto a Signal IK and Device Id, both of which are generated randomly during device setup. The Sylo IK, in combination with the Device Id, derives a valid Signal Protocol address which is used for establishing Signal Protocol sessions between peers’ devices.

Device initialization

A device is comprised of the following components.

The Device ID A randomly generated numerical device identifier unique to the Sylo IK

The Host Identity Key A randomly generated key-pair used in peer to peer communications on the underlying libp2p network.

The Signal Identity Key A randomly generated key-pair used as the Signal Protocol identity key for asynchronous end-to-end encrypted exchange of information.

With these values at hand, Sylo Protocol continues to advertise the device as a device operated under the Sylo IK by crafting an entry in the peer’s profile log, which concludes device registration.

Integration reference

Sylo Protocol is designed to be run as a daemon, with any number of clients connecting/talking to it.

The language talked between the daemon and connecting clients is a custom, protobuf-based RPC protocol that sports support for command cancellation, events, and extensive error handling.

Running a daemon requires a node.js environment and expects to be hooked up to a transport medium.

Example showing how to hook the daemon up in a node.js worker thread

import SP from '@sylo/protocol-v2'
import { Worker, isMainThread, parentPort } from 'worker_threads'

if (isMainThread) {
  const worker = new Worker(__filename)
  /* ...use worker to communicate with daemon... */
} else {
  /* start the daemon */
  SP.run(
    {
      /* daemon configuration */
    },
    message => {
      /* feed RPC-produced messages to the host */
      parentPort.postMessage({
        command: 'feed',
        payload: message
      })
    }
  ).then(runner => {
    /* (optional) observe runner */
    runner.join().then(
      () => {
        /* the daemon has terminated */
      },
      error => {
        /* the daemon has terminated erroneously */
      }
    )

    /* feed messages the worker receives to the runner */
    parentPort.onmessage = event => {
      switch (event.data.command) {
        case 'kill': {
          runner.kill()
          return
        }
        case 'feed': {
          runner.feed(event.data.payload)
          return
        }
      }
    }
  })
}

The RPC protocol

All messages going back and forth between the daemon and clients take the following shape.

enum MessageClass {
  REQUEST = 0;       // Send to daemon to make an RPC request.
  CANCEL = 1;        // Send to daemon to cancel an RPC request.
  RESPONSE = 2;      // Send to client to indicate the result of a request.
  EVENT = 3;         // Send to client to notify an event.
}

// An RPC protocol packet.
message Message {
  // Used to match up responses with their associated requests.
  string requestId = 1;
  MessageClass messageClass = 2;

  // Contains a serialized protobuf which may be a Request or a Response. This
  // field should never be empty.
  bytes payload = 3;
}

Issuing a Request

All types of request carry a unique identifier, which is provided along side the request’s parameter payload, if the command takes parameters.

enum RequestCode {
  UNKNOWN = 0; // Default value.
  IDENTITY_GENERATE = 1;
  IDENTITY_IMPORT = 2;
  IDENTITY_LIST = 3;

  /* ... <omitted many more> ... */
}

message Request {
  RequestCode code = 1;  // Which request to make.

  // A serialized protobuf containing the request parameters. Can be empty if
  // there are no parameters.
  bytes parameters = 2;
}

Add types of requests further associate a sum of errors that may occur, a successful response type, and - finally - an optional type of event that running the request may cause.

For example, the IdentityGenerate request

All valid requests/commands have backing protobuf messages as per example below. For a complete reference, please refer to either the api documentation or the Sylo Protocol sources.

/**
 * Generate a new Sylo identity key and initialize a new Sylo device.
 */
message IdentityGenerateParameters {
}

message IdentityGenerateError {
  oneof error {
    /** Device initialization failed */
    .Empty initializeError = 1;
  }
}

message IdentityGenerateResponse {
  /** The generated sylo identity */
  .identity.Identity identity = 1 [(purs.required)=true];
  /** The generated device */
  .device.Device device = 2 [(purs.required)=true];
}