git ssb

1+

Dominic / secure-scuttlebutt



Tree: 113223327508da436eab9a15c25935b6e5e18258

Files: 113223327508da436eab9a15c25935b6e5e18258 / README.md

11211 bytesRaw

secure-scuttlebutt

A database of unforgable append-only feeds, optimized for efficient replication for peer to peer protocols.

What does it do?

Secure-scuttlebutt provides tools for dealing with unforgeable append-only message feeds. You can create a feed, post messages to that feed, verify a feed created by someone else, stream messages to and from feeds, and more (see API).

"Unforgeable" means that only the owner of a feed can modify that feed, as enforced by digital signing (see Security properties). This property makes secure-scuttlebutt useful for peer-to-peer applications. Secure-scuttlebutt also makes it easy to encrypt messages.

Example

In this example, we create a feed, post a signed message to it, then create a stream that reads from the feed.

/**
 * create a secure scuttlebutt instance and add a message to it.
 */

var pull = require('pull-stream')

// paths:
var pathToDB     = '/tmp/ssb1/'
var pathToSecret = '/tmp/ssb1-secret'

// ways to create keys:
var keys = require('ssb-keys').generate()
var keys = require('ssb-keys').loadSync(pathToSecret)
var keys = require('ssb-keys').createSync(pathToSecret)
var keys = require('ssb-keys').loadOrCreateSync(pathToSecret)

// create the db instance.
//  - uses leveldb.
//  - can only open one instance at a time.

var ssb = require('secure-scuttlebutt/create')(pathToDB)

// create a feed.
//  - this represents a write access / user.
//  - you must pass in keys.
//  (see options section)

var feed = ssb.createFeed(keys)

// publish a message.
//  - feed.add appends a message to your key's chain.
//  - the `type` attribute is required.

feed.add({ type: 'post', text: 'My First Post!' }, function (err, msg, hash) {
  // the message as it appears in the database:
  console.log(msg)

  // and its hash:
  console.log(hash)
})

// stream all messages for all keypairs.
pull(
  ssb.createFeedStream(),
  pull.collect(function (err, ary) {
    console.log(ary)
  })
)

// stream all messages for a particular keypair.
pull(
  ssb.createHistoryStream(feed.id),
  pull.collect(function (err, ary) {
    console.log(ary)
  })
)

Concepts

Building upon secure-scuttlebutt requires understanding a few concepts that it uses to ensure the unforgeability of message feeds.

Identities

An identity is simply a public/private key pair.

Even though there is no worldwide store of identities, it's infeasible for anyone to forge your identity. Identities are binary strings, so not particularly human-readable.

Feeds

A feed is an append-only sequence of messages. Each feed is associated 1:1 with an identity. The feed is identified by its public key. This works because public keys are unique.

Since feeds are append-only, replication is simple: request all messages in the feed that are newer than the latest message you know about.

Note that append-only really means append-only: you cannot delete an existing message. If you want to enable entities to be deleted or modified in your data model, that can be implemented in a layer on top of secure-scuttlebutt using delta encoding.

Messages

Each message contains:

Since each message contains a reference to the previous message, a feed must be replicated in order, starting with the first message. This is the only way that the feed can be verified. A feed can be viewed in any order after it's been replicated.

Object ids

The text inside a message can refer to three types of secure-scuttlebutt entities: messages, feeds, and blobs (i.e. attachments). Messages and blobs are referred to by their hashes, but a feed is referred to by its signing public key. Thus, a message within a feed can refer to another feed, or to a particular point within a feed.

Object ids begin with a sigil @ % and & for a feedId, msgId
and blobId respectively.

Note that secure-scuttlebutt does not include facilities for retrieving a blob given the hash.

Replication

It is possible to easily replicate data between two SecureScuttlebutts. First, they exchange maps of their newest data. Then, each one downloads all data newer than its newest data.

Scuttlebot is a tool that makes it easy to replicate multiple SecureScuttlebutts using a decentralized network.

Security properties

Secure-scuttlebutt maintains useful security properties even when it is connected to a malicious secure-scuttlebutt database. This makes it ideal as a store for peer-to-peer applications.

Imagine that we want to read from a feed for which we know the identity, but we're connected to a malicious secure-scuttlebutt instance. As long as the malicious database does not have the private key:

API

ssb = require('secure-scuttlebutt/create')(path)

Create a secure-scuttlebutt database at the given path, returns an instance.

require('secure-scuttlebutt')(db, opts)

Pass in a levelup instance (it must have sublevel installed), and an options object. The options object provides the crypto and encoding functions, that are not directly tied into how secure-scuttlebutt works.

The following methods all apply to a SecureScuttlebutt instance

SecureScuttlebutt#createFeed (keys?)

Create a Feed object. A feed is a chain of messages signed by a single key (the identity of the feed). This handles the state needed to append valid messages to a feed. If keys are not provided, then a new key pair will be generated.

The following methods apply to the Feed type.

Feed#add (message, cb)

Adds a message of a given type to a feed. This is the recommended way to append messages. message is a javascript object. It must be a {} object with a type
property that is a string between 3 and 32 chars long.

Feed#id

the id of the feed (which is the feed's public key)

Feed#keys

the key pair for this feed.

SecureScuttlebutt#needsRebuild(cb)

Checks the version stored in the database against the code version and calls back true/false accordingly. This keeps the database in sync with major breaking changes to secure-scuttlebutt when they occur.

Should be run at startup. If true, you should call rebuildIndex before using the database.

ssb.needsRebuild(function (err, b) {
  if (b)
    ssb.rebuildIndex(next)
})

SecureScuttlebutt#rebuildIndex(cb)

Rebuilds the indexes by replaying history. See needsRebuild.

SecureScuttlebutt#createFeedStream (opts) -> PullSource

Create a pull-stream
of all the feeds in the database, ordered by timestamps. All pull-level options are allowed (start, end, reverse, tail)

SecureScuttlebutt#createLogStream({gt: ts, tail: boolean}) -> PullSource

create a stream of the messages that have been written to this instance in the order they arrived. This is mainly intended for building views. The objects in this stream will be of the form:

{
  key: Hash, value: Message, timestamp: timestamp
}

timestamp is generated by monotonic-timestamp

SecureScuttlebutt#createHistoryStream ({id: feedId, seq: int?, live: bool?}) -> PullSource

Create a stream of the history of id. If seq > 0, then only stream messages with sequence numbers greater than seq. if live is true, the stream will be a live mode

SecureScuttlebutt#messagesByType ({type: string, live: bool?}) -> PullSource

retrive messages with a given type. All messages must have a type, so this is a good way to select messages that an application might use. Returns a source pull-stream. This function takes all the options from pull-level#read
(gt, lt, gte, lte, limit, reverse, live)

Get a stream of links from a feed to a blob/msg/feed id.

The objects in this stream will be of the form:

{ source: feedId, rel: String, dest: Id, key: MsgId, value: Object? }

If opts.values is set (default: false) value will be the message the link occurs in. If opts.keys is set (default: true) key will be the message id. If opts.meta is unset (default: true) source, hash, rel will be left off.

Note: if source, and dest is provided, but not rel, ssb will have to scan all the links from source, and then filter by dest. your query will be more efficient if you also provide rel.

SecureScuttlebutt#relatedMessages ({id: msgId, rel: string?, count: false?, parent: false?}, cb)

Retrieve the tree of messages related to id. This is ideal for collecting things like threaded replies. If rel is provided, only messages that link to the message with the given type are included. The output is a recursive structure like this:

{
  key: <msgId>,
  value: <msg>,
  related: [
    <recursive>,...
  ],
  //number of messages below this point. (when opts.count = true)
  count: <int>,
  //the message this message links to. this will not appear on the bottom level.
  //(when opts.parent = true)
  parent: <parent_id>
}

If count option is true, then each message will contain a count
it's descendant messages. If parent is true then each level will have parent, the id/key of it's parent message.

Stability

Stable: Expect patches, possible features additions.

License

MIT

Built with git-ssb-web