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 thePromise
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];
}