Introducing, 3Box Ghost Threads API

Performant, In-Memory, Decentralized Messaging

3Box Labs
Published in
7 min readOct 15, 2019

--

Performant, In-Memory, Decentralized Messaging

We’re thrilled to introduce the latest addition to the 3Box Threads API, Ghost Threads: a scaleable, performant, in-memory messaging framework, great for creating decentralized chatrooms.

What are Ghost Threads? 👻

Ghost threads are a different type of messaging thread, that unlike current 3Box threads, do not persist messages to OrbitDB database storage. Instead, ghost messages are sent from one peer to other peers on the network using IPFS pubsub, and are kept in-memory upon receipt by online peers. New users coming online or joining the chatroom can request a message backlog from other users in the chat.

This Ghost Threads API allows Ethereum app developers to create new chat features that demand scalable performance to support many users sending many messages such as site-wide chatrooms, lightning fast load speeds, or novel properties such as disappearing messages.

Getting Started with Ghost Threads

Ghost Threads v. Persistent Threads

The 3Box Threads API now offers two variations: Ghost and Persistent. Each thread architecture has its benefits, use cases, and tradeoffs. Let’s explore what they mean for your application.

Architecture

Ghost threads is a scalable, performant, in-memory messaging framework great for chatrooms, built with 3Box and libp2p. Unlike persistent threads, ghost messages are not stored and persisted in an OrbitDB database (which can get very long if there are many messages), but rather are sent from one user to others on the network via ipfs/libp2p pubsub rooms and are kept in-memory by online peers. New users can request a message backlog from online peers, so the message history persists as long as at least one user is online at any time.

Because 3Box does not connect to the public IPFS network, but rather utilizes a private IPFS network, we cannot directly rely on the infrastructure provided by the Protocol Labs team. Therefore we operate an IPFS websocket rendezvous server to provide a way for peers to discover other peers in Ghost Threads. Going forward we are also looking to operate a delegate router which will allow js-ipfs nodes to more quickly discover peers by querying the DHT through a delegate node.

Persistent threads, on the other hand, keep messages in an OrbitDB feed store that persists messages in an append-only log that is shared amongst participants. As long as participants know the identifier of the thread, they can always sync and access messages stored in the thread even if no other client peers are online, since the 3Box pinning infrastructure is always online and can be used to sync the latest version of the thread.

Benefits and Tradeoffs

Ghost Threads are best suited for chatrooms that have one or more of the following requirements: many messages, lots of activity, fast load speeds, or disappearing messages.

The drawback to Ghost Threads is that messages are weakly persisted, since they only exist ephemerally in peer memory. If all peers simultaneously go offline, the message history will be lost.

Persistent Threads are best suited for comment systems or message/content feeds that have one of more of the following requirements: strong message persistence guarantees; high availability guarantees; can tolerate offline peers; can support members-only write permissions; can support personal feeds.

The drawback to Persistent Threads is that threads are less scalable and chatty, since every message is saved in an append-only OrbitDB log which can get very long. This can cause persistent threads to load slowly on the client at scale.

What can I build with Ghost Threads?

While there are endless possibilities for what you can build using the new Ghost Threads API, here are a few ideas to get you started.

Chatrooms: Ghost Threads will make it super simple to add scalable, high-volume chatrooms, site-wide chats, or even “trollboxes” to your Ethereum application.

Disappearing messages: Since messages only exist in-memory between online peers, it would be easy to create an application where online users can exchange messages with each other that disappear when both go offline. This can be done site-wide, or as a direct ghost thread between two or multiple users.

Direct messages: The Ghost Threads API allows for users to post messages directly to another peer id or 3ID (DID), which means that you can potentially allow users in a ghost chatroom to direct message (DM) each other on the side.

How to Build with Ghost Threads

Using the steps below, you can add Ghost Threads functionality to your application to create decentralized chatrooms and more. Alternatively, you can read the docs.

We will soon release a Chatbox plugin that allows you to add a Ghost Threads-enabled chatroom or “trollbox” to your react Ethereum application with only a few lines of code. This will be the easiest way to get started experimenting with Ghost Threads.

1. Install 3Box SDK

npm i 3box

2. Import and authenticate 3Box

import Box from '3box';const box = await Box.openBox(myAddress, window.ethereum, {});

3. Configure your new Ghost Thread

You only need to perform this step if you are creating a new Ghost Thread. If you want to join an existing thread, then proceed to the next step.

Before getting started, you should consider a few configuration options for your Ghost Thread, which will make later steps more simple. You only need to perform this step if you are creating a new Ghost Thread. If you want to join an existing thread, proceed to the next step.

Confirm you need a Ghost Thread

The first step in configuring a Ghost Thread is making sure you actually need a Ghost Thread, and not a Persistent Thread. If you’re unsure which to implement, you can read the tradeoffs at the beginning of this post.

Choose a name for your Space

Ghost Threads are access controlled using 3Box spaces. These spaces are created and exist inside the 3Box of each user that joins the Ghost Thread, not in one single global space. Before proceeding you should choose a name for the space that you will use to store your app’s Ghost Threads.

In most cases, the name of the space should be the name of your application.

We recommend simply using the name of your application for this threads space, unless you want your application’s threads managed by a different space for some reason. This space is an OrbitDB key-value store, so you can also keep other information and data related to your application inside it.

For example, if your application is called CheeseWizards, we would recommend your space be named cheeseWizards.

Choose a naming convention for your Threads

All Ghost Threads are uniquely identified by their space name and thread name. If either of these variables differs, you will get an entirely different thread.

For thread name, we recommend creating some standard convention that allows your application to easily create and manage many threads. While you can decide what this convention is, you may consider using some URL, or a topic, or something else unique.

4. Join a new or existing Ghost Thread

Authenticate the Space

The first step in interacting with Ghost Threads is to authenticate the Space where the thread(s) will be accessed and referenced. This should have already been done in the overall flow of adding 3Box to your web app, but if you haven’t first authenticated the Space for your Ghost Threads, use this code to do so:

const space = await box.openSpace('mySpace')

Note: If the user already has a space with this name, your application will gain access to it; if the user does not have the space, this method will automatically create one for them.

Join a new or existing Ghost Thread

Whether you’re creating a new Ghost Thread or joining an existing one, you will need the name of the Ghost Thread to begin reading and adding messages to it. In the code example below, let space be the space from the previous step, and 'myThread' be your specific thread name.

Use the space.joinThread() method to join a new or existing Ghost Thread:

const thread = await space.joinThread('myThread', {  ghost: true,  ghostBacklogLimit: 20 // optional and defaults to 50})

5. View posts of a Ghost Thread

In Ghost Threads, if you are a new member you will want to request a backlog of messages from online peers before doing thread.getPosts().

Display current posts in a Ghost Thread

To get all posts in a thread, use thread.getPosts().

const posts = await thread.getPosts()console.log(posts)// you can also specify a number of posts you wantconst posts = await thread.getPosts(20)console.log(posts)

Listen for updates to the Ghost Thread

To listen for updates to a thread, such as new posts, use thread.onUpdate(). This will allow you to immediately display new messages posted in the thread in your application.

thread.onUpdate(() => {  const posts = await thread.getPosts()  console.log(posts)})

6. View Members in a Ghost Thread

List current members in a Thread

The listMembers() method will return an array of space 3IDs for users in the chatroom.

const userList = await thread.listMembers()

Update when members join or leave the Thread

Use this code to optionally post a 'User joined/left the chat' message to the thread when new users join or leave:

thread.onNewCapabilities((event, did) => console.log(did, event, ' the chat'))

The event parameter is a string that is either: joined or left.

7. Add posts to a Ghost Thread

Broadcast message to all peers

Everyone who can read the thread will have access to the message.

await thread.post('hello everyone')

8. Leave a Ghost Thread

await thread.close()

Want to learn more?

We’re always available on Discord to help answer your questions. Drop by if you have questions or if you need help integrating. Join us!

--

--

3Box Labs
3Box Labs

Software for a more open, safe and collaborative web. We’re building Ceramic Network and IDX.