• Stars
    star
    1,310
  • Rank 35,926 (Top 0.8 %)
  • Language
    JavaScript
  • License
    Other
  • Created over 12 years ago
  • Updated over 7 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

peer-to-peer replicatable data structure

scuttlebutt

A base-class for real-time replication.

travis

browser support

This seems like a silly name, but I assure you, this is real science. Read this: http://www.cs.cornell.edu/home/rvr/papers/flowgossip.pdf

Or, if you're lazy: http://en.wikipedia.org/wiki/Scuttlebutt (laziness will get you nowhere, btw)

secure-scuttlebutt

Creating this module eventually lead me to secure-scuttlebutt it is based on a similar replication protocol as scuttlebutt, but puts security at the forefront.

Subclasses

Scuttlebutt is intended to be subclassed into a variety of data-models.

Two implementations are provided as examples scuttlebutt/model and scuttlebutt/events

subclasses:

Replication

Any Scuttlebutt subclass is replicated with createStream. Create a server and then connect a client to it:

var Model = require('scuttlebutt/model')
var net   = require('net')

var s = new Model()
var z = new Model()

net.createServer(function (stream) {

  stream.pipe(s.createStream()).pipe(stream)

}).listen(8000, function () {

  //then connect the client!
  var stream = net.connect(8000)
  stream.pipe(z.createStream()).pipe(stream)  

})

Gotchas

Scuttlebutt is always duplex. Scuttlebutt does a handshake on connecting to another scuttlebutt, and this won't work unless both sides are connected.

Right

stream.pipe(model.createStream()).pipe(stream)

WRONG!

wrongStream.pipe(model2.createStream())

Also, when creating a server, scuttlebutt needs a stream for EACH connection.

Right

net.createServer(function (stream) {
  stream.pipe(model.createStream()).pipe(stream)
}).listen(port)

WRONG!

this will use one stream for many connections!

var wrongStream = model.createStream()
net.createServer(function (stream) {
  stream.pipe(wrongStream).pipe(stream)
}).listen(port)

Errors and use in PRODUCTION

If have are using scuttlebutt in production, you must register on 'error' listener in case someone sends invalid data to it.

** Any stream that gets parsed should have an error listener! **

net.createServer(function (stream) {
  var ms = m.createStream()
  stream.pipe(ms).pipe(stream)
  ms.on('error', function () {
    stream.destroy()
  })
  stream.on('error', function () {
    ms.destroy()
  })
}).listen(9999)

Otherwise, if someone tries to connect to port 9999 with a different protocol (say, HTTP) this will emit an error. You must handle this and close the connection / log the error.

Also, you should handle errors on stream, stream may error if the client responsible for it crashes.

Persistence

Persist by saving to at least one writable stream.

var Model = require('scuttlebutt/model') //or some other subclass...
var fs = require('fs')
var m = new Model()

//stream FROM disk.
fs.createReadStream(file).pipe(m.createWriteStream())

//stream TO disk.
m.on('sync', function () {
  m.createReadStream().pipe(fs.createWriteStream(file))
})

Use on('sync',... to wait until the persisted state is in the file before writing to disk. (Make sure you rotate files, else there is an edge case where if the process crashes before the history has been written some data will be lost /this is where link to module for that will go/)

You may use kv to get streams to local storage.

read only mode.

Sometimes you want to use scuttlebutt to send data one way, from a master instance to a slave instance.

var s1 = master.createStream({writable: false, sendClock: true})
var s2 = slave.createStream({readable: false, sendClock: true})

master will emit updates, but not accept them, over this stream. This checking is per stream - so it's possible to attach master to another master node and have master nodes replicate each way.

Implementing Custom Scuttlebutts

A custom Scuttlebutt is a data model that inherits from Scuttlebutt. It must provide an implementation of history() and applyUpdate().

See r-value for a demonstration of the simplest possible Scuttlebutt, one that replicates a a single value.

Scuttlebutt#history(sources)

sources is a hash of source_ids: timestamps. History must return an array of all known events from all sources That occur after the given timestamps for each source.

The array MUST be in order by timestamp.

Scuttlebutt#applyUpdate (update)

Each update is of the form [change, timestamp, source] (see Protocol below).

Possibly apply a given update to the subclasses model. Return 'true' if the update was applied. (See scuttlebutt/model.js for an example of a subclass that does not apply every update.)

Scuttlebutt#createStream (opts)

Create a duplex stream to replicate with a remote endpoint.

The stream returned here emits a special 'header' event with the id of the local and remote nodes and the vector clock. You can set metadata on the header object using opts.meta.

Examples

Connect two Model scuttlebutts locally.

var Model = require('scuttlebutt/model')

var a = new Model()
var b = new Model()

a.set(key, value)

b.on('update', console.log)

var s = a.createStream()
s.pipe(b.createStream()).pipe(s)

scuttlebutt/events

A reliable event emmitter. Multiple instances of an emitter may be connected to each other and will remember events, so that they may be present after a disconnection or crash.

With this approach it is also possible to persist events to disk, making them durable over crashes.

var Emitter = require('scuttlebutt/events')
var emitter = new Emitter()

emit (event, data)

Emit an event. Only one argument is permitted.

on (event, listener)

Add an event listener.

scuttlebutt/model

A replicateable Model object, a simple key-value store.

var Model = require('scuttlebutt/model')
var model = new Model()

get (key)

Get a property.

set (key, value)

Set a property.

on('update', function ([key, value], source, updateId))

Emmitted when a property changes. If source !== this.id then it was a remote update.

Protocol

Messages are sent in this format:

[change, timestamp, source]

source is the id of the node which originated this message. Timestamp is the time when the message was created. This message is created using Scuttlebutt#localUpdate(key, value).

When two Scuttlebutts are piped together, they both exchange their current list of sources. This is an object of {source_id: latest_timestamp_for_source_id} After receiving this message, Scuttlebutt sends any messages not yet known by the other end. This is the heart of Scuttlebutt Reconciliation.

Security

Scuttlebutt has an (optional) heavy duty security model using public keys. This enables a high level of security even in peer-to-peer applications. You can be sure that a given message is from the node that sent it, even if you did not receive the messasge from them directly.

Enabling Security

var model = require('scuttlebutt/model')
var security = require('scuttlebutt/security')
var keys = {}
var m = new Model(security(keys, PRIVATE, PUBLIC))

Security API

When security is enabled, each scuttlebutt message is signed with a private key. It is then possible for any scuttlebutt instance to be confident about the authenticity of the message by verifying it against the source's public key.

This is possible even if the verifying node received the message from an intermediate node.

Security is activated by passing in a security object to the contructor of a scuttlebutt subclass.

Use the included implementation:

var security = require('scuttlebutt/security')(keys, PRIVATE, PUBLIC)
var Model = require('scuttlebutt/model')

var m = new Model(security)

See scuttlebutt/security.js for a simple example implementation.

sign(update) should sign the update with the instance's private key. verify(update, cb) should verify the update, using public key associated with the source field in the update. Verification may be asynchronous. verify must callback cb(err, boolean) where boolean indicates whether or not the signature is valid. Only callback in error in the most extreme circumstances. If there was no known key for the required source then that should be treated as a verification failure. If it is not possible to reach the key database (or whatever) then the request should be retried until it is available.

Note: although the API supports asynchronous verification, it's probably a good idea to load keys into memory so that messages can be verified and signed synchronously.

createId() returns a new id for the current node. This is used in the example security implementation to return a id that is a hash of the public key. This makes it impossible for rogue nodes to attempt to associate a old node id with a new public key.

Generating Keys.

Generate an ssh private key, and a PEM encoded public key.

ssh-keygen -f $KEYNAME -b $LENGTH -N $PASSWORD -q
ssh-keygen -e -f $KEYNAME.pub -m PEM > $KEYNAME.pem

$LENGTH must be >= 786, shorter is faster but less secure. password may be empty ''.

$KEYNAME is the private key, and $KEYNAME.pem is the public key to use with Scuttlebutt.

More Repositories

1

event-stream

EventStream is like functional programming meets IO
JavaScript
2,189
star
2

JSON.sh

a pipeable JSON parser written in Bash
Shell
1,996
star
3

JSONStream

rawStream.pipe(JSONStream.parse()).pipe(streamOfObjects)
JavaScript
1,913
star
4

rc

The non-configurable configuration loader for lazy people.
JavaScript
995
star
5

crdt

Commutative Replicated Data Types for easy collaborative/distributed systems.
JavaScript
836
star
6

through

simple way to create a ReadableWritable stream that works
JavaScript
667
star
7

your-web-app-is-bloated

measuring memory usage of popular webapps
514
star
8

npmd

JavaScript
450
star
9

split

JavaScript
346
star
10

curry

simple curry module, with nothing *too clever*, and full test coverage
JavaScript
313
star
11

random-name

JavaScript
296
star
12

hashlru

JavaScript
240
star
13

wifi.sh

Shell
216
star
14

level-sublevel

no longer maintained, sorry!
JavaScript
194
star
15

mux-demux

mutiplex-demultiplex multiple streams through a single text Stream
JavaScript
179
star
16

noderify

official fork: https://github.com/staltz/noderify
JavaScript
157
star
17

feedopensource

Iteratively Fund Open Source Projects With Bitcoin
JavaScript
142
star
18

excel-stream

JavaScript
137
star
19

stream-spec

executable specification for Stream (make testing streams easy)
JavaScript
125
star
20

map-stream

JavaScript
122
star
21

map-reduce

async map-reduce functions for nodejs
JavaScript
121
star
22

cyphernet

115
star
23

observable

A Mutable Value represented as a Function.
HTML
111
star
24

stream-combiner

JavaScript
103
star
25

rpc-stream

JavaScript
98
star
26

bench-lru

JavaScript
87
star
27

pull-box-stream

One way streaming encryption based on libsodium's secretbox primitive
JavaScript
84
star
28

level-live-stream

JavaScript
79
star
29

stack-expression

inspired by regular expressions but can do nested structures
JavaScript
76
star
30

hipster

JavaScript
72
star
31

snob

distributed version control system implemented in javascript.
JavaScript
71
star
32

xdiff

diff complex javascript objects
JavaScript
70
star
33

from

Easy way to create a Readable Stream
JavaScript
70
star
34

scalable-secure-scuttlebutt

HTML
68
star
35

explain-error

JavaScript
67
star
36

fsm

Finite State Machines in javascript
JavaScript
66
star
37

r-edit

JavaScript
64
star
38

readme

JavaScript
62
star
39

tiles

JavaScript
61
star
40

indexhtmlify

JavaScript
59
star
41

tacodb

JavaScript
57
star
42

adiff

diff and patch operations on arrays.
JavaScript
57
star
43

map-filter-reduce

JavaScript
57
star
44

browser-stream

open pipable streams to and from the browser, with Socket.io
JavaScript
55
star
45

reconnect

JavaScript
53
star
46

level-replicate

JavaScript
51
star
47

electro

JavaScript
51
star
48

d64

JavaScript
50
star
49

on-change-network

JavaScript
49
star
50

lock

lock asynchronous resources
JavaScript
48
star
51

crypto-bench

HTML
47
star
52

mynosql

JavaScript
44
star
53

monotonic-timestamp

JavaScript
44
star
54

pause-stream

JavaScript
43
star
55

json-select

JavaScript
43
star
56

json-buffer

JavaScript
41
star
57

coherence

JavaScript
41
star
58

bittodo

JavaScript
40
star
59

stream-punks

discussion repo for streams
39
star
60

charwise

JavaScript
39
star
61

proxy-by-url

custom logic for node-http-proxy to proxy based on incoming url
JavaScript
38
star
62

sentimental-versioning

version numbers with meaning
HTML
38
star
63

level-hooks

JavaScript
37
star
64

sodium-browserify

JavaScript
37
star
65

secret-handshake-paper

TeX
36
star
66

browselectrify

create browserify bundle that also works in electron
JavaScript
36
star
67

kv

simple kv store for streams
JavaScript
35
star
68

c2wasm

C++
35
star
69

level-trigger

triggers for levelup
JavaScript
33
star
70

deploy

scripts to setup continuous deployment with git push
Shell
33
star
71

presentations

JavaScript
32
star
72

rumours

Intergration of scuttlebutt family.
JavaScript
32
star
73

web-bootloader

HTML
28
star
74

what-is-scuttlebutt

spec for defining "scuttlebutt" as a living changing protocol
28
star
75

remote-events

connect EventEmitters through Streams.
JavaScript
28
star
76

indexes-of

JavaScript
27
star
77

mpg123

JavaScript
27
star
78

level-master

JavaScript
27
star
79

h

JavaScript
26
star
80

testbed

continuous integration for nodejs
JavaScript
25
star
81

canvas-browserify

HTML
25
star
82

it-is

assertion DSL based on functional idioms.
JavaScript
25
star
83

level-merkle

JavaScript
25
star
84

semver-ftw

Simple Description of SemVer
HTML
25
star
85

level-inverted-index

JavaScript
24
star
86

computer-modern

CSS
24
star
87

hyperaudio

JavaScript
24
star
88

level-search

JavaScript
24
star
89

level-scuttlebutt

leveldb persistence for scuttlebutts (scuttlebutt/crdt/append-only and friends)
JavaScript
24
star
90

level-couch-sync

JavaScript
23
star
91

simple-xlsx

maintained fork is at https://github.com/zeke/simple-xlsx
JavaScript
23
star
92

shasum

JavaScript
23
star
93

content-addressable-store

JavaScript
23
star
94

ticket-auth

JavaScript
22
star
95

ssh-key-to-pem

JavaScript
21
star
96

private-groups-paper

21
star
97

scuttlebucket

JavaScript
21
star
98

looper

JavaScript
20
star
99

deterministic-tar

JavaScript
20
star
100

npm-browserify

JavaScript
20
star