• Stars
    star
    103
  • Rank 333,046 (Top 7 %)
  • Language
    JavaScript
  • License
    MIT License
  • Created about 10 years ago
  • Updated over 1 year ago

Reviews

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

Repository Details

A single interface that can work with multiple protocols, and multiple transforms of those protocols (eg, security layer)

multiserver

A single interface that can work with multiple protocols, and multiple transforms of those protocols (eg, security layer)

motivation

Developing a p2p system is hard. Especially hard is upgrading protocol layers. The contemporary approach is to update code via a backdoor, but as easily as security can be added, it can be taken away.

Before you can have a protocol, you need a connection between peers. That connection is over some form of network transport, probably encrypted with some encryption scheme, possibly compression or other layers too.

Usually, two peers connect over a standard networking transport (probably tcp) then they have a negotiation to decide what the next layer (of encryption, for example) should be. This allows protocol implementators to roll out improved versions of the encryption protocol. However, it does not allow them to upgrade the negotiation protocol! If a negotiation protocol has a vulnerability it's much harder to fix, and since the negotiation needs to be unencrypted, it tends to reveal a lot about program the server is running. in my opinion, it's time to try a different way.

Some HTTP APIs provide upgradability in a better, simpler way by putting a version number within the url. A new version of the API can then be used without touching the old one at all.

multiserver adapts this approach to lower level protocols. Instead of negotiating which protocol to use, run multiple protocols side by side, and consider the protocol part of the address.

Most network systems have some sort of address look up, there is peer identifier (such it's domain) and then a system that is queried to map that domain to the lower level network address (such as it's ip address, retrieved via a DNS (Domain Name System) request) To connect to a website secured with https, first you look up the domain via DNS, then connect to the server. Then start a tls connection to that server, in which a cyphersuite is negotiated, and a certificate is provided by the server. (this certifies that the server really owns that domain)

If it was using multiserver, DNS would respond with a list of cyphersuites, (encoded as multiserver addresses) and then you'd connect directly to a server and start using the protocol, without negotiation. p2p systems like scuttlebutt also usually have a lookup, but usually mapping from a public key to an ip address. Since a look up is needed anyway, it's a good place to provide information about the protocol that server speaks!

This enables you to do two things, upgrade and bridging.

upgrade

If a peer wants to upgrade from weak protocol to a strong one, they simply start serving strong via another port, and advertise that in the lookup system. Now peers that have support for strong can connect via that protocol.

Once most peers have upgraded to strong, support for weak can be discontinued.

This is just how some services (eg, github) have an API version in their URL scheme. It is now easy to use two different versions in parallel. later, they can close down the old API.

var MultiServer = require('multiserver')
var chloride = require('chloride')
var keys = chloride.crypto_sign_keypair()
var appKey = "dTuPysQsRoyWzmsK6iegSV4U3Qu912vPpkOyx6bPuEk="

function accept_all (id, cb) {
  cb(null, true)
}
var ms = MultiServer([
  [ //net + secret-handshake
    require('multiserver/plugins/net')({port: 3333}),
    require('multiserver/plugins/shs')({
      keys: keys,
      appKey: appKey, //application key
      auth: accept_all
    }),
  ],
  [ //net + secret-handshake2
    //(not implemented yet, but incompatible with shs)
    require('multiserver/plugins/net')({port: 4444}),
    //this protocol doesn't exist yet, but it could.
    require('secret-handshake2')({
      keys: keys,
      appKey: appKey, //application key
      auth: accept_all
    }),
  ]
])

console.log(ms.stringify())

//=> net:<host>:3333~shs:<key>;net:<host>:4444~shs2:<key>

//run two servers on two ports.
//newer peers can connect directly to 4444 and use shs2.
//this means the protocol can be _completely_ upgraded.
ms.server(function (stream) {
  console.log('connection from', stream.address)
})

//connect to legacy protocol
ms.client('net:<host>:3333~shs:<key>', function (err, stream) {
  //...
})

//connect to modern protocol
ms.client('net:<host>:4444~shs2:<key>', function (err, stream) {
  //...
})

bridging

By exposing multiple network transports as part of the same address, you can allow connections from peers that wouldn't have been able to connect otherwise.

Regular servers can do TCP. Desktop clients can speak TCP, but can't create TCP servers that other desktop computers can connect to reliably. Browsers can use WebSockets and WebRTC. WebRTC gives you p2p, but needs an introducer. Another option is utp

  • probably the most convenient, because it doesn't need an introducer on every connection (but it does require some bootstrapping), but that doesn't work in the browser either.
var MultiServer = require('multiserver')

var ms = MultiServer([
  require('multiserver/plugins/net')({port: 1234}),
  require('multiserver/plugins/ws')({port: 2345})
])

//start a server (for both protocols!)
//returns function to close the server.
var close = ms.server(function (stream) {
  //handle incoming connection
})

//connect to a protocol. uses whichever
//handler understands the address (in this case, websockets)
var abort = ms.client('ws://localhost:1234', function (err, stream) {
  //...
})

//at any time abort() can be called to cancel the connection attempt.
//if it's called after the connection is established, it will
//abort the stream.

address format

Addresses describe everything needed to connect to a peer. each address is divided into protocol sections separated by ~. Each protocol section is divided itself by :. A protocol section starts with a name for that protocol, and then whatever arguments that protocol needs. The syntax of the address format is defined by multiserver-address

For example, the address for my ssb pubserver is:

net:wx.larpa.net:8008~shs:DTNmX+4SjsgZ7xyDh5xxmNtFqa6pWi5Qtw7cE8aR9TQ=

That says use the net protocol (TCP) to connect to the domain wx.larpa.net on port 8008, and then encrypt the session using shs (secret-handshake) to the public key DTNmX+4SjsgZ7xyDh5xxmNtFqa6pWi5Qtw7cE8aR9TQ=.

Usually, the first section is a network protocol, and the rest are transforms, such as encryption or compression.

Multiserver makes it easy to use multiple protocols at once. For example, my pub server also supports shs over websockets.

So, this is another way to connect:

wss://wx.larpa.net~shs:DTNmX+4SjsgZ7xyDh5xxmNtFqa6pWi5Qtw7cE8aR9TQ=

if your server supports multiple protocols, you can concatenate addresses with ; and multiserver will connect to the first address it understands.

net:wx.larpa.net:8008~shs:DTNmX+4SjsgZ7xyDh5xxmNtFqa6pWi5Qtw7cE8aR9TQ=;wss://wx.larpa.net~shs:DTNmX+4SjsgZ7xyDh5xxmNtFqa6pWi5Qtw7cE8aR9TQ=

This means use net, or wss. In some contexts, you might have a peer that understands websockets but not net (for example a browser), as long as a server speaks at least one protocol that a peer can understand, then they can communicate.

scopes

address also have a scope. This relates to where they can be connected to. Default supported scopes are:

  • device - can connect only if on the same device
  • local - can connect from same wifi (local network)
  • public - can connect from public global internet.

some transport plugins work only on particular scopes.

when stringify(scope) is called, it will return just the accessible addresses in that scope.

plugins

A multiserver instance is set up by composing a selection of plugins that construct the networking transports, and transforms that instance supports.

There are two types of plugins, transports and transforms.

net({port,host,scope})

TCP is a net:{host}:{port} port is not optional.

var Net = require('multiserver/plugins/net')`
Net({port: 8889, host: 'mydomain.com'}).stringify()  => 'net:mydomain.com:8889'
Net({port: 8889, host: 'fe80::1065:74a4:4016:6266:4849'}).stringify()  => 'net:fe80::1065:74a4:4016:6266:4849:8889'
Net({port: 8889, host: 'fe80::1065:74a4:4016:6266:4849', scope: 'device'}).stringify()  => 'net:fe80::1065:74a4:4016:6266:4849:8889'

WebSockets({host,port,scope,handler?,key?,cert?})

create a websocket server. Since websockets are just a special mode of http, this also creates a http server. If opts.handler is provided, requests to the http server can be handled, this is optional.

WebSockets ws://{host}:{port}? port defaults to 80 if not provided.

WebSockets over https is wss://{host}:{port}? where port is 443 if not provided.

If opts.key and opts.cert are provided as paths, a https server will be spawned.

var WebSockets = require('multiserver/plugins/ws`)

var ws = WebSockets({
  port: 1234,
  host: 'mydomain.com',
  handler: function (req, res) {
    res.end('<h1>hello</h1>')
  },
  scope:...
})

ws.stringify() => 'ws://mydomain.com:1234'

Onion()

Connect over tor using local proxy to dæmon (9050) or tor browser (9150). Both will be tried to find a suitable tor instance. The tor ports are unconfigurable. The standard tor ports are always used.

This plugin does not support creating a server. You should use tor's configuration files to send incoming connections to a net instance as a hidden service.

An accepted onion address looks like: onion:{host}:{port} port is not optional. This plugin does not return an address, so you must construct this address manually.

var Onion = require('multiserver/plugins/onion`)


var onion = WebSockets({
  //no config is needed except scope, but you
  //surely will use this with "public" which is the default
  //scope:'public'
})

ws.stringify() => null

Bluetooth({bluetoothManager})

The multiserver-bluetooth module implements a multiserver protocol for to communicate over Bluetooth Serial port.

reactnative = require('multiserver-rn-channel')

The multiserver-rn-channel module implementes a multiserver protocol for use inbetween the reactnative nodejs process and browser process.

SHS({keys,timeout?,appKey,auth})

Secret-handshake is shs:{public_key}:{seed}?. seed is used to create a one-time shared private key, that may enable a special access. For example, you'll see that ssb invite codes have shs with two sections following. Normally, only a single argument (the remote public key) is necessary.

var SHS = require('multiserver/plugins/shs')

var shs = SHS({
  keys: keys,
  timeout: //set handshake timeout, if unset falls through to secret-handshake default
  appKey: //sets an appkey
  auth: function (id, cb) {
    if(isNotAuthorized(id))
      cb(new Error())
    else
      cb(null, authenticationDetails)
  }
})
shs.stringify() => 'shs:{keys.publicKey.toString('base64')}

note, if the auth function calls back a truthy value, it is considered authenticated. The value called back may be an object that represents details of the authentication. when a successful connection goes through shs plugin, the stream will have an auth property, which is the value called back from auth, and a remote property (the id of remote key).

Noauth({keys})

This authenticates any connection without any encryption. This should only be used on device scoped connections, such as if net is bound strictly to localhost, or a unix-socket. Do not use with ws or net bound to public addresses.

var Noauth = require('multiserver/plugins/noauth')

var noauth = Noauth({
  keys: keys
})
shs.stringify() => 'shs:{keys.publicKey.toString('base64')}

streams passing through this will look like an authenticated shs connection.

Unix = require('multiserver/plugins/unix-socket')

network transport is unix socket. to connect to this you must have access to the same file system as the server.

var Unix = require('multiserver/plugins/unix-socket')

var unix = Unix({
  path: where_to_put_socket,
  scope: ... //defaults to device
})

unix.stringify() => "unix:{where_to_put_socket}"

createMultiServer([[transport,transforms...],...])

A server that runs multiple protocols on different ports can simply join them with ; and clients should connect to their preferred protocol. clients may try multiple protocols on the same server before giving up, but generally it's unlikely that protocols should not fail independently (unless there is a bug in one protocol).

an example of a valid multiprotocol: net:{host}:{port}~shs:{key};ws:{host}:{port}~shs:{key}

var MultiServer = require('multiserver')

var ms = MultiServer([
  [net, shs],
  [ws, shs],
  [unix, noauth]
])

ms.stringify('public') => "net:mydomain.com:8889~shs:<key>;ws://mydomain.com:1234~shs:<key>"
ms.stringify('device') => "unix:{where_to_put_socket}"

ms.server(function (stream) {
  //now that all the plugins are combined,
  //ready to use as an actual server.
})

interfaces

To construct a useful multiserver instance, one or more transport is each connected with zero or more transforms. The combine function is the default export from the multiserver module.

var MultiServer = require('multiserver')

var ms = MultiServer([
  [transport1, transform1],
  [transport2, transform2, transform3],
])

var close = ms.server(function (stream) {
  //called when a stream connects
}, onError, onListening)
createMultiServer([[Transform, Transports*,...]], *]) => MultiServer

a MultiServer has the same interface as a Transport, but using a combined multiserver instance as a transport is not supported.

createTransport(Options) => Transport

The transport exposes a name and the ability to create and connect to servers running that transport.

Transport => {
  // that describes the sub protocols
  name,
  // connect to server with address addr.
  client (addr, cb),
  // start the server
  server (onConnect, onError, onListening),
  // return string describing how to connect to the server, aka, "the address"
  // the address applies to a `scope`.
  stringify(scope),
  // parse the addr,
  // normally this would probably return the
  // Options used to create the transport.
  parse(string) => Options
}

createTransform(options) => Transform

Transform => {
  name: string,
  create(Options) => (stream, cb(null, transformed_stream)),
  parse (str) => Options,
  stringify() => string,
}

note the create method on a Transform takes Options, and returns a function that takes a stream and a callback, and then calls back the transformed stream. In all cases the stream is a duplex stream

License

MIT

More Repositories

1

patchwork

A decentralized messaging and sharing app built on top of Secure Scuttlebutt (SSB).
JavaScript
3,582
star
2

ssb-server

The gossip and replication server for Secure Scuttlebutt - a distributed social network
JavaScript
1,677
star
3

ssb-db

A database of unforgeable append-only feeds, optimized for efficient replication for peer to peer protocols
JavaScript
1,175
star
4

patchbay

An alternative Secure Scuttlebutt client interface that is fully compatible with Patchwork
JavaScript
387
star
5

scuttlebutt-protocol-guide

Protocol documentation for Secure Scuttlebutt
HTML
213
star
6

go-ssb-room

Room server implemented in Go
Go
187
star
7

handbook.scuttlebutt.nz

ssb handbook: A guide to the Secure Scuttlebutt key concepts and influences (see also, new website: https://gitlab.com/ssbc/scuttlebutt.nz/)
CSS
165
star
8

go-ssb

Go implementation of ssb (work in progress!)
Go
155
star
9

epidemic-broadcast-trees

bandwidth efficient broadcast gossip
JavaScript
120
star
10

muxrpc

lightweight multiplexed rpc
JavaScript
98
star
11

chloride

JavaScript
90
star
12

secret-stack

connect peers to each other using secret-handshakes
JavaScript
89
star
13

patchcore

A shared library of depject modules to build Secure Scuttlebutt social network apps
JavaScript
75
star
14

docs

Documentation repo
Shell
61
star
15

patchfoo

Plain SSB web UI. Uses HTML forms instead of client-side JS. Designed for use on low-power and low-resource computers.
54
star
16

scuttle-shell

A system tray app for running Secure Scuttlebutt and providing sbot features to your local system
JavaScript
54
star
17

jitdb

A database on top of a log with automatic index generation and maintenance
JavaScript
49
star
18

ssb-db2

A new database for secure-scuttlebutt
JavaScript
47
star
19

ssb-client

client library to scuttlebot
JavaScript
46
star
20

bipf

Binary json codec optimized for in-place access
JavaScript
45
star
21

go-secretstream

Go port of secret-handshake
Go
44
star
22

ssb-keys

keyfile operations for ssb
JavaScript
36
star
23

dgram-broadcast

Formerly known as "broadcast-stream" on npm
JavaScript
34
star
24

dynamic-dijkstra

JavaScript
31
star
25

tinySSB

tinySSB is "Secure Scuttlebutt over LoRa and BLE"
C
29
star
26

ssb-peer-invites

A new ssb invite system to create invites without having a pub
JavaScript
26
star
27

ssb-tribes

JavaScript
26
star
28

go-muxrpc

js/muxrpc in golang (for interfacing ssb/scuttlebot)
Go
23
star
29

ssb-friends

Manages the SSB social graph
JavaScript
23
star
30

ssb-config

standard configuration for ssb
JavaScript
23
star
31

packet-stream

JavaScript
23
star
32

ssb-tunnel

create a p2p link tunneled through a pub server
JavaScript
22
star
33

ssb-ahoy

An onboarding mini-app - gets you all set up, and caught up on the gossip before you set out on your adventure
JavaScript
22
star
34

sips

Secure Scuttlebutt Implementation Protocols
HTML
22
star
35

netsim

secure scuttlebutt network simulator
Go
21
star
36

rooms2

Design doc for the next edition of SSB Room servers
HTML
21
star
37

dev.scuttlebutt.nz

The go-to place for updated developer information on the SSB stack in different languages
20
star
38

ssb-threads

Scuttlebot plugin for fetching messages as threads
JavaScript
20
star
39

multiblob

A content-addressable-store that supports multiple hashing algorithms, and pull-streams
JavaScript
20
star
40

envelope-spec

JavaScript
18
star
41

patchlite

[WIP] A browser lite client for the Scuttlebutt network
JavaScript
18
star
42

ssb-ebt

secure scuttlebutt replication with epidemic-broadcast-trees
JavaScript
18
star
43

ssb-identities

manage multiple identities as sbot plugin
JavaScript
18
star
44

ssb2-discussion-forum

not quite tiny, also not quite large
17
star
45

ssb-uri-spec

Specification of SSB URIs
16
star
46

async-append-only-log

A new append-only-log for SSB purposes
JavaScript
16
star
47

visual-docs

Diagrams and animations documenting Secure Scuttlebutt (scuttlebutt.nz) and Δ€hau (ahau.io)
JavaScript
16
star
48

ssb-conn

SSB plugin for establishing and managing peer connections
TypeScript
16
star
49

ssb-typescript

Contains type definitions for common SSB concepts
TypeScript
16
star
50

margaret

a flume-like persisted append-only log implementation
Go
16
star
51

ssb-spec-drafts

A collection of protocol specifications for Secure Scuttlebutt
HTML
15
star
52

ssb-first-aid-kit

A user-friendly app for diagnosing and fixing problems with your Scuttlebutt installation
Vue
15
star
53

fusion-identity-spec

14
star
54

ssb-ooo

retrive ssb messages Out Of Order
JavaScript
14
star
55

ssb-subset-replication-spec

14
star
56

private-group-spec

JavaScript
13
star
57

ssb-graphviz

visualize your ssb network graph
JavaScript
12
star
58

ssb-viewer

JavaScript
12
star
59

ssb-validate

better ssb validator
JavaScript
12
star
60

ssb-group-exclusion-spec

12
star
61

ssb-query

JavaScript
12
star
62

ssb-hello-ws

simplest example to get a ssb-client working over websockets
HTML
12
star
63

envelope-js

new message encryption for ssb
JavaScript
12
star
64

layered-graph

JavaScript
11
star
65

ssb-blobs

blob gossiping ssb-subprotocol
JavaScript
11
star
66

ssb-buttwoo-spec

Spec for a new binary feed format for SSB
11
star
67

ssb-backup-tool

A backup tool for the Scuttleverse
JavaScript
11
star
68

scuttlebot.io

Source repo for https://scuttlebot.io
JavaScript
11
star
69

ssb-meta-feeds-spec

Design doc for subfeeds in SSB
HTML
11
star
70

ssb-usage-stats

Generate statistics about network use (number of users, growth, retention, etc)
JavaScript
11
star
71

ssb-meta-feeds

JavaScript
10
star
72

ssb-ws

ssb-ws & http server for ssb
JavaScript
10
star
73

ssb-uri2

Utilities for recognizing and converting SSB URIs
JavaScript
10
star
74

ssb-fixtures

Generate a simulated .ssb folder
TypeScript
10
star
75

ssb-gossip

Schedule connections randomly with a peerlist constructed from config, multicast UDP announcements, feed announcements, and API-calls
JavaScript
10
star
76

scuttle-testbot

JavaScript
10
star
77

ssb-bfe-rs

Binary Field Encodings (BFE) for Secure Scuttlebutt (SSB)
Rust
10
star
78

ssb-http-auth-spec

Specification of SSB HTTP Authentication
HTML
10
star
79

ssb-backlinks

scuttlebot plugin for indexing all link mentions of messages
JavaScript
10
star
80

ssb-markdown

patchwork's markdown parser
JavaScript
9
star
81

bipf-spec

Spec for bipf
9
star
82

ssb-bfe-spec

JavaScript
9
star
83

ssb-links

ssb-plugin that indexes all the links!
JavaScript
9
star
84

ssb-replicate

ssb legacy replication, previously built into ssb-server
JavaScript
8
star
85

ssb-conn-hub

Module that manages active connections to SSB peers
TypeScript
8
star
86

ssb-tribes2

SSB private groups with ssb-db2
JavaScript
8
star
87

ssb-device-address

announce a public address for yourself
JavaScript
8
star
88

stack-diagram

A diagram detailing the core components utilized in the SSB stack
8
star
89

ssb-sort

JavaScript
8
star
90

ssb-serve-blobs

Sbot plugin to serve blobs from a local http server
JavaScript
8
star
91

packet-stream-codec

JavaScript
8
star
92

ssb-search2

Full-text search in SSB using ssb-db2
TypeScript
8
star
93

ssb-replication-scheduler

Plugin to trigger replication of feeds identified as friendly in the social graph
JavaScript
8
star
94

ssb-tangle

JavaScript
8
star
95

ssb-private

scuttlebot plugin for indexed private messages
JavaScript
8
star
96

ssb-invite

"followbot" style invite codes for ssb
JavaScript
8
star
97

ssb-meta-feeds-migration-spec

How to migrate from classic SSB to metafeeds
7
star
98

ssb-caps

The default "Caps" keys for accessing the SSB protocol using secret handshake
7
star
99

go-metafeed

A go implementation of the bendy-butt format spec to support ssb metafeeds
Go
7
star
100

ssb-meme

JavaScript
7
star