@peerio/peerio-icebear

2.11.1

WELCOME

Welcome to Peerio Icebear SDK documentation.

HTML version of this documentation contains all documented members.

  • lock icon denotes member private to parent module, it's not exported from module.
  • cogs icon denotes protected member for SDK internal use, it's exported from module, but is not exported from index
  • question icon denotes unspecified access level (should be fixed)

Markdown version contains only items marked as @public and is not as well organized as HTML version, sort order is different.

LOCALIZATION DOCS: Peerio uses its own localization library, see documentation here https://github.com/PeerioTechnologies/peerio-translator/tree/icebear

CONFIGURATION

Icebear exports configuration object that you have to initialize with some mandatory properties and configure some optional ones.

The recommended pattern to deal with configuration:

  1. Create config.js in source root of your project
    // import icebear config
    const config = require('icebear').config;

    // set and override properties
    config.icebearProperty = 'value';
    config.clientSpecificProperty = 'value'

    // and export extended config object in the end
    module.exports = config;
  1. Require this config.js at your app startup so it extends Icebear config.

Configuration module. Exists just to collect most of the app configuration aspects in one place.

Following properties have to be set before client app starts using Icebear SDK. Best to do it in your local config.js

  • socketServerUrl
  • ghostFrontendUrl
  • appVersion
  • platform
  • FileStream
  • StorageEngine
config
Static Members
upload.chunkSizes
upload.getChunkSize(fileSize)
upload.encryptBufferSize
upload.uploadBufferSize
upload.uploadBufferSize
debug.trafficReportInterval
debug.socketLogEnabled
socketServerUrl
ghostFrontendUrl
appVersion
platform
CHUNK_OVERHEAD
download.maxDownloadChunkSize
download.maxDecryptBufferSize
FileStream
StorageEngine
observableClockEventFrequency
serverPlans
serverPlansPremium
serverPlansPro
chat.maxInitialChats
chat.initialPageSize
chat.pageSize
chat.maxLoadedMessages
chat.decryptQueueThrottle
chat.recentFilesDisplayLimit
chat.maxChatNameLength
chat.maxChatPurposeLength

ENTRY POINT

To use Icebear SDK you need to require its entry point at any time you need it. Icebear will initialize whatever needs to be initialized, you just have to start the socket connection once, whenever your application is ready.

const icebear = require('icebear');
icebear.socket.start();

Entry point exports all the public API you will need.

In addition to exporting public API, entry point, when first required, performs some global configuration such as:

  • replaces global Promise object with bluebird implementation. Note that native(not transpiled) async functions will still return native Promise.
  • extends Uint8Array prototype. See extensions/uint8array.
index
Instance Members
_handle2faOnLogin()
signout()

MISC

Miscellaneous items

Virtual type representing asymmetric key pair.

KeyPair

Type: Object

Properties
publicKey (Uint8Array) : 32 bytes
secretKey (Uint8Array) : 32 bytes or 64 bytes in case of signing key pair

Virtual type representing address as server sends it.

Address

Type: Object

Properties
address (string)
confirmed (boolean)
primary (boolean)
type (string) : currently always == 'email'

InvitedContact

src/typedefs.js

Virtual type representing invited contact. Username appears when invited contact joins Peerio.

InvitedContact

Type: Object

Properties
email (string)
added (number)
username (string?)

extensions/uint8array

src/extensions/uint8array.js

Uint8Array extensions and polyfills.

extensions/uint8array
Static Members
slice(begin, end)

Peerio custom error types and error handling helpers.

// CUSTOM ERROR TEMPLATE
function CustomError(message, nestedError, otherData) {
  var error = Error.call(this, message);
  this.name = 'CustomError';
  this.message = error.message;
  this.stack = error.stack;
  this.nestedError = nestedError;
  this.otherData = otherData;
}

CustomError.prototype = Object.create(Error.prototype);
CustomError.prototype.constructor = CustomError;

REFACTOR WARNING: before renaming any errors (not sure why you would do that though), make sure they haven't been used by name anywhere.

errors
Static Members
normalize(error, failoverMessage?)
getGenericCustomError(name, msg)
serverErrorCodes
new ServerError(code, msg?)

Various utility functions that didn't fit anywhere else.

util
Static Members
convertBuffers(obj)
formatBytes(bytes)

CRYPTO

Crypto modules export publicly everything they have for internal icebear use. Normally you don't need to use it because Icebear handles all the encryption/keys/signing on its own. But in case your application requires some special logic you have the full crypto toolkit for it.

Peerio crypto utilities module. Exported from icebear index as crypto.cryptoUtil

crypto/util
Static Members
strToBytes(str)
bytesToStr(bytes)
b64ToBytes(str)
bytesToB64(bytes)
bytesToHex(bytes)
hexToBytes(str)
numberToByteArray(num)
byteArrayToNumber(arr, offset, length)
getHashObject(length, value, personalizationString?)
getHexHash(length, value, personalizationString?)
getByteHash(length, value, personalizationString?)
getFingerprint(username, publicKey)
fingerprintToStr(bytes)
concatTypedArrays(arr1, arr2)
MAX_PASSPHRASE_LENGTH
padBytes(arr, length)
padPassphrase(passphrase)
unpadPassphrase(paddedPassphrase)
getRandomBytes(num)
getRandomNumber(min, max)
getRandomNonce()
getRandomUserSpecificIdBytes(username)
getRandomUserSpecificIdB64(username)
getRandomUserSpecificIdHex(username)

Peerio Crypto module for key handling.

crypto/keys
Static Members
scryptPromise(value, salt, options)
prehashPass(value, personalization?)
deriveAccountKeys(username, passphrase, randomSalt)
deriveEphemeralKeys(salt, passphrase)
deriveKeyFromPasscode(username, passcode)
generateSigningKeyPair()
generateEncryptionKeyPair()
generateEncryptionKey()
generateAuthSalt()
getAuthKeyHash(key)

Secret key encryption module.

Encrypt and decrypt functions replace nacl.secretbox and nacl.secretbox.open see tweetnacl-js https://github.com/dchest/tweetnacl-js. This replacement reduces the amount of memory allocation and copy operations.

The output cipher bytes have following differences with nacl.secretbox output:

  • nonce is appended to the cipher bytes.
  • 16 BOXZEROBYTES in the beginning of cipher bytes are not stripped and another 16 are appended to them because we'll need them for decryption

Cipherbytes structure: [ 32 zero bytes ][ actual cipher bytes ][ 24-byte nonce]

crypto/secret
Static Members
NONCE_SIZE
encrypt(msgBytes, key, nonce, appendNonce, prependLength)
encryptString(msg, key)
decrypt(cipher, key, nonce, containsLength)
decryptString(cipher, key)

Public key encryption module

crypto/public
Static Members
decryptCompat(cipher, nonce, theirPublicKey, mySecretKey)
encrypt(msgBytes, theirPublicKey, mySecretKey)
decrypt(cipher, theirPublicKey, mySecretKey)
computeSharedKey(theirPublicKey, mySecretKey)

Digital signing module

crypto/sign
Static Members
signDetached(message, secretKey)
verifyDetached(message, signature, publicKey)
setImplementation(signFunc, verifyFunc)

crypto/scrypt-proxy

src/crypto/scrypt-proxy.js

Mobile UI thread suffocates even with async scrypt so we let mobile implement scrypt in a worker thread.

crypto/scrypt-proxy
Static Members
getScrypt()
setScrypt(fn)

STORAGE

TinyDb is an global instance of TinyDbManager with storage engine specified in config.

TinyDb
Example
// at any time use unencrypted shared collection
TinyDb.system.getValue('lastAuthenticatedUsername');
// after successful login use User's personal encrypted database.
// Only values are encrypted.
TinyDb.user.setValue('lastUsedEmoji',':grumpy_cat:')

This is a contract for the actual client-specific StorageEngine client has to implement and set to config module. TinyDb will get the implementation from config module and use it.

StorageEngineInterface
Parameters
namespace (string) unique namespace will be passed to storage engine when instantiating. Values in current instance should be stored under that unique namespace.
Instance Members
getValue(key)
setValue(key, value)
removeValue(key)
getAllKeys()
clear()

This is a StorageEngine implementation that can be used in any nodejs-based apps (cli, electron).

It uses os.homedir() or NodeJsonStorage.storageFolder to store JSON files.

new NodeJsonStorage(name: any)
Parameters
name (any)

UTILITY CLASSES

Observable clock. Provides clock.now property that is mobx observable and changes at specified time interval. Doesn't tick when no one is observing. Create your own clock or use default one.

new Clock(interval: number)
Parameters
interval (number) clock update interval in seconds
Static Members
default
Instance Members
now
dispose()

Observable timer counter up/down.

new Timer()
Instance Members
counter
countUp(seconds)
countDown(seconds)
stop()

Base class for any Most Recently Used implementations. Gotcha: Don't create 2+ instances for the same list name. Due to caching it will lead to conflicts.

new MRUList(name: string, limit: number?)
Parameters
name (string) unique name for the list
limit (number? = 10 ) maximum number of elements in the list (will remove least recent)
Instance Members
list
loadCache()
_saveCache
addItem(item)

Provides access to array of Uint8Array chunks of data in a stream fashion. Currently supports only reading from stream. Underlying array can receive new data by appending(pushing) chunks to it

new ArrayStream(array: Array<Uint8Array>)
Parameters
array (Array<Uint8Array>)
Instance Members
peek(size)
read(size, adjust = true)
adjustPos(size)
length

HELPERS

Various kinds of helper modules

helpers/system-messages

src/helpers/system-messages.js

Helpers for dealing with chat system messages. For UI use.

Some chat messages are in fact sent automatically by client and instead of text they carry special systemData codes. We don't want clients to repeat handling logic of those codes when system message has to be rendered, this module should be used instead.

helpers/system-messages
Static Members
getSystemMessageText(msg)

Various file helpers

helpers/file
Static Members
getFileName(path)
getFileNameWithoutExtension(path)
getFileExtension(path)

String helpers

helpers/string
Static Members
getFirstLetter(str)
getFirstLetterUpperCase(str)

Retry operation tools.

helpers/retry
Static Members
retryUntilSuccess(fn, id = Math.random(), maxRetries, thisIsRetry?)

helpers/prombservable

src/helpers/prombservable.js

Observable/Promise bridges and tools

helpers/prombservable
Static Members
asPromise(object, observableProperty, expectedValue)

helpers/dynamic-array-map

src/helpers/dynamic-array-map.js
helpers/dynamic-array-map
Static Members
createMap(array, keyProp)

helpers/di-current-user

src/helpers/di-current-user.js

DI module to use models/user avoiding cyclic requires

helpers/di-current-user
Static Members
setUser(user)
getUser()

helpers/di-file-store

src/helpers/di-file-store.js

DI module to use models and stores avoiding cyclic requires

helpers/di-file-store
Static Members
setFileStore(store)
getFileStore()

helpers/di-contact-store

src/helpers/di-contact-store.js

DI module to use models and stores avoiding cyclic requires

helpers/di-contact-store
Static Members
setContactStore(store)
getContactStore()
helpers/field-validation
Static Members
addValidation(store, fName, validatorOrArray, positionInForm?)
helpers/user-validators
Static Members
_callServer(context, name, value)

NETWORK

Main connection SocketClient instance.

Normally this is the only instance you should use. It gets connection url from config and you have to call socket.start() once everything is ready.

socket

Use socket.js to get the default instance of SocketClient, unless you do need a separate connection for some reason.

SocketClient emits many events, main ones are:

  • started - whenever socket.start() is called the first time.
  • connect - every time connection has been established.
  • authenticated - when connection is fully authenticated and ready to work.
  • disconnect - every time connection has been broken.

The rest you can find in sources:

  • SOCKET_EVENTS - whatever is happening with socket.io instance
  • APP_EVENTS - server emits them
new SocketClient()
Instance Members
socket
started
url
connected
preauthenticated
authenticated
throttled
reconnectAttempt
reconnecting
latency
reconnectTimer
bytesReceived
bytesSent
requestId
awaitingRequests
authenticatedEventListeners
startedEventListeners
STATES
SOCKET_EVENTS
APP_EVENTS
start(url)
state
setAuthenticatedState()
validateSubscription(event, listener)
subscribe(event, listener)
unsubscribe(event, listener)
send(name, data)
cancelAwaitingRequests()
onceConnected(callback)
onceAuthenticated(callback)
onceStarted(callback)
onAuthenticated(handler)
onDisconnect(handler)
close
open
resetReconnectTimer
reset

COMMON LOGIC

This is the place where Icebear can get various state information about client and client can provide such information. Also works as container for high level properties we couldn't find better place for.

ClientApp
Static Members
isFocused
isInChatsView
isInFilesView
clientVersionDeprecated
active2FARequest
updatingAfterReconnect
Instance Members
create2FARequest(type, submitCallback, cancelCallback)

Passphrase dictionary module.

new PhraseDictionary(locale: string, dictString: string)
Parameters
locale (string) locale code for dict
dictString (string) '\n' separated word list
Static Members
current
setDictionary(localeCode, rawData)
Instance Members
getPassphrase(length)
dispose()

Some client configuration details can't be hardcoded to clients or stored in every user database. This module takes care of these settings by loading them from server every time client connects. There's no need for 'updated' events from server because when these settings change server always resets connection.

ServerSettings
Static Members
avatarServer
acceptableClientVersions
tag
loadSettings()

Data update tracking module. This is an internal module that allows Icebear to get and report new data as it arrives and is needed by your client.

How does update tracking work:

  1. Update Tracker interacts with application logic via a. UpdateTracker.digest object - at any time, app logic can read data from that object, although it's not always guaranteed to be fully up to date, but it is not a problem because: b. Update events - update events are triggered in an optimized(batched) manner
  2. Every time connection is authenticated, Update Tracker performs update of relevant data (cuz we might have missed it while disconnected). We don't do full update info reload because it has a potential to grow really big. a. SELF database info is always reloaded b. anything that is {unread: true} is reloaded c. anything that contains {kegType: 'important keg type'} is reloaded d. anything that is currently active in the UI (chat) is reloaded
UpdateTracker
Instance Members
dbAddedHandlers
updateHandlers
digest
getDigest(id, type)
onKegDbAdded(handler)
onKegTypeUpdated(kegDbId, kegType, handler)
unsubscribe(handler)
emitKegDbAddedEvent(id)
emitKegTypeUpdatedEvent(id, type)
flushAccumulatedEvents
processDigestResponse
loadDigest
seenThis(id, type, updateId)

SYSTEM WARNINGS

Warnings module solves the problem of various asynchronous notification about various events that Icebear needs to do.

There are 2 kinds of system warnings at the moment. 1. Server warnings. Server sends a translation key from a restricted amount of allowed keys (serverWarning_ prefix) and a unique id for the warning. Icebear generates a system warning and, after it's dismissed, informs server that user has acknowledged this warning. 2. System warnings. Any Icebear subsystem or your application code can generate a warning with any available translation key and optionally some data for that key.

Base/local class for warnings. Server warnings class inherits from it. You don't need to instantiate it directly, Icebear warnings module has a factory for that.

new SystemWarning(content: string, title: string?, data: Object?, level: string, callback: any)
Parameters
content (string) localization string key
title (string?) localization string key
data (Object?) variables to pass to peerio-translator when resolving content
level (string = 'medium' ) severity level, options (medium, severe)
callback (any)
Static Members
STATES
Instance Members
state
show()
dismiss()
autoDismiss()
cancelAutoDismiss()
dispose()

Server warning. Server sends locale key and severity level for client to display. You don't need to create instances of this class, Icebear takes care of it.

new ServerWarning(obj: Object, onClear: function)

Extends SystemWarning

Parameters
obj (Object) warning object as received from server
Name Description
obj.msg string translation key starting with serverWarning_ for security any other keys will be ignored
obj.title string same as 'msg' but for dialog title
obj.level string 'medium' or 'severe'
obj.token string unique id of this warning to send it back and dismiss this warning
onClear (function) callback will be called when warning is successfully dismissed on server

Public API for Warnings system.

Warnings
Static Members
current
Instance Members
queue
sessionCache
queueItem(warning)
assignNextItem
add(content, title?, data?, level, callback?)
addSevere(content, title?, data?, callback?)
addServerWarning(serverObj)

KEGS ENGINE

Base class with common metadata and operations.

new Keg(id: string?, type: string, db: KegDb, plaintext: boolean, forceSign: boolean, allowEmpty: boolean, storeSignerData: boolean)
Parameters
id (string?) kegId, or null for new kegs
type (string) keg type
db (KegDb) keg database instance owning this keg
plaintext (boolean = false ) should keg be encrypted
forceSign (boolean = false ) plaintext kegs are not normally signed unless forceSign is true
allowEmpty (boolean = false ) normally client doesn't expect empty keg when calling .load() and will throw
storeSignerData (boolean = false ) if the keg is signed, in addition to signature it will store and then verify over signedByUsername prop instead of keg.owner .
Instance Members
type
db
plaintext
overrideKey
version
format
collectionVersion
props
forceSign
allowEmpty
storeSignerData
signatureError
sharedKegError
tempId
deleted
loading
dirty
lastLoadHadError
ignoreAntiTamperProtection
isEmpty
assignTemporaryId()
saveToServer(cleanShareData)
internalSave(cleanShareData)
signKegPayload(payload)
load()
remove()
loadFromKeg(keg)
validateAndReEncryptSharedKeg(kegProps)
verifyKegSignature(payload, props)
serializeKegPayload()
deserializeKegPayload(payload)
serializeProps()
deserializeProps(props)
detectTampering(payload)

Named plaintext Boot keg for 'SELF' databases.

new BootKeg(db: KegDb, bootKey: Uint8Array)

Extends Keg

Parameters
db (KegDb) owner instance
bootKey (Uint8Array)
Instance Members
signKeys
encryptionKeys

Named plaintext Boot keg for shared keg databases.

Payload format version 1:

{
  publicKey: b64 encoded buffer,
  encryptedKeys: {
          username: b64 encoded buffer, // encrypted for user's PK
          username1: b64 encoded buffer
        }
}

Payload format version 2:

{
  publicKey: b64 encoded buffer, // public key from the encrypting ephemeral pair
  roles: {
     admin: ["username", "username1", ...],
     some_role_we_will_want_later: ["username"]
  },
  // object key = incremental key id
  encryptedKeys: {
     "0": {
             createdAt: number
             keys: {
                // key id
                username: {
                    key: b64 encoded buffer, // encrypted for user's PK
                    publicKey: b64 encoded buffer // user's public key (so user can still
                                                  // decrypt this after she changes her keys)
                }
                username1: {...}
             }
          },
     "1": {
          ...
        }
  }
}
  1. Adding someone to the chat with full history access

    • Create new 'username:encryptedKey' record in the most ALL key object
    • for this generate new ephemeral key pair, and re-encrypt all the stuff
    • Send a system message 'username was invited' At this point, invited user has full normal access to this keg db, BUT doesn't receive any notifications push or keg notifications. As user now has full access to the keg db she will infer invitation status from the special keg described below.
  2. Revoking invite or removing user from the channel

    • Delete the record for this user from ALL keys
    • Server deletes invite_status-username keg
  3. Accepting an invitation

    • invited user creates/undeletes invite_status-username keg with accepted:true property in it Server enables notifications when joined:true.
  4. Leaving a channel

    • participant sets joined:false
    • admin removes user from boot keg

invite_status keg

{
  accepted: boolean
}
new ChatBootKeg(db: KegDb, user: User)

Extends SyncedKeg

Parameters
db (KegDb) owner instance
user (User) currently authenticated user
Static Members
addKey()
assignRole(contact, role)
unassignRole(contact, role)
Instance Members
kegKey
kegKeyId
keys
participants
admins
addParticipant(contact)
removeParticipant(contact)
onSaved()

Keg database. This class is for user's own database ('SELF')

new KegDb()
Instance Members
id
key
keyId
createBootKeg(bootKey, signKeys, encryptionKeys, kegKey)