Giter Site home page Giter Site logo

multiserver's Introduction

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

multiserver's People

Contributors

arj03 avatar austinfrey avatar christianbundy avatar cryptix avatar dangerousbeans avatar dominictarr avatar garbados avatar happy0 avatar lancew avatar mmckegg avatar moimikey avatar progval avatar regular avatar staltz avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

multiserver's Issues

IPv6 addresses are not escaped inside net: addresses

If I do

sbot server --host=:: --port=8100 --ws.port=8101&
sbot getAddress -- --host=::1 --port=8100

I get:

net::::8100~ ... ;ws://[::]:8101

Is that desired behaviour because the square brackets are needed because the ws address is actually a URL and the net: address is not? Or is it a bug and it should be net:[::]:8100? cc @dominictarr

why do multiserver addresses not include the "full" public key?

i notice that multiserver addresses, like

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

truncate the "full" public key to

DTNmX+4SjsgZ7xyDh5xxmNtFqa6pWi5Qtw7cE8aR9TQ=

instead of

@DTNmX+4SjsgZ7xyDh5xxmNtFqa6pWi5Qtw7cE8aR9TQ=.ed25519

as is the format used everywhere else.

for more context, this became annoying when i wanted to write a jinja template for file managed by Salt Stack, where i had access to the values in ~/.ssb/secret but had no ability to truncate the public key as required to create a multiserver address in the template.

i've now worked around this problem, but i still was wondering on the thinking behind it, i don't know much about how important it is to have the curve in the key, as i think this is also implied when saying shs.

Why does noauth need the public key?

I see this commit, but I don't understand what's the context around that commit. What breaks if we use empty strings instead?

My context: given that I'm using trusted transports (e.g. react native's "bridge" which is closed to other applications in the OS, and obviously closed to remote actors), I believe I don't need the public key, hence I could save the cost of using FS APIs on the frontend looking for secret, but the way noauth plugin is built requires me to pass it a public key.

create multiple tor services?

@arj03 is it possible to create multilpe tor hidden services via this api?

Oh, hmm - I just realized that this currently sets server: false which means this doesn't even create a server.... this creates the ability to use tor as a client protocol... but how do are you configuring the server?

asking because I am working on a less hardcoded api for using the various network protocols.

address format

Need to figure out an address format.

I'm thinking that something like
(type(:args)?)(,type(:args)?)*

where , is a separator, type is a protocol name (ws or net or utp etc) args is whatever information type needs (optional - for example gzip doesn't need args)

ipfs uses / like a unix path, but I think this is ambigious because you have to know how many arguments a type takes to be able to parse it! That is especially bad for an upgradable system where you may see a descriptor for a thing that you don't support yet, and won't know what it means. It may mean you'll need to do IO while parsing.

instead better to use a different seperator, and allow protocols to format their args however they need, (provided any internal separators are escaped)

'|' would be nice, because it's a lot like piping, but `,' means you couldn't need to escape it on the terminal.

Address format

Looking at: https://github.com/ssbc/multiserver#address-format I see that net is specified as net:, while wss is specified as wss://? Seems as if both should be specified the same way, so wss:wx.larpa.net~shs:DTNmX+4SjsgZ7xyDh5xxmNtFqa6pWi5Qtw7cE8aR9TQ=, instead of wss://wx.larpa.net~shs:DTNmX+4SjsgZ7xyDh5xxmNtFqa6pWi5Qtw7cE8aR9TQ=?

net: bind to internal host, return external host

so i'm working on the ButtCloud infrastructure (butt), i finally figured out a bug that's kept me stuck on that front for over a week.

it seems i need a way for the scuttlebot server to bind to a host like 0.0.0.0 (internal to the Docker Swarm service, which is automatically available to other internal services and exported via an ingress router to the external internet), but then advertise a different public host like party.butt.nz (like when publishing invites and things).

scopes

I'm a bit confused by the scope thing. what i want:

I want a message that indicates your public address, but unlike the current "pub" messages, you should only speak for your self, not claim someone else's pub address. This would be used for pubs, and also, for peers behind tunnels, or introduced, etc...

so, basic pattern will be:

command to advertise public address:
check your public address. (configured or detected)
check if you have already posted a message about this.
if yes, do nothing.
If not, or the address has changed since then,
then publish a message.


when creating a user invite, query for peers (which maybe be someone's pub) that follows you closely (in hops distance) that also has a public address. these are the peers which are likely to service the invite, so attach several of these to the code, say 3.


but poking around, on my laptop I get "net:192.168.43.244:8008~shs:EMovhfIrFk4NihAKnRNhrfRaqIhBv1Wj8pTxJNgvCCY="
which is only a local network address...
but on my pub, I get "net:128.199.132.182:8008~shs:DTNmX+4SjsgZ7xyDh5xxmNtFqa6pWi5Qtw7cE8aR9TQ="
which is good, that is a public address... but there is no way to tell from calling that method which sort of address it will be?

For example: I want to advertise the 192.168... address on a local wifi, but not on a public feed, but the other one I want on a public feed. do we need more distinctions than just public and private?

@arj03 what do you think?

net 'external' should be an array

The SSB-Config documentation says that external is an array of external strings, but the Multiserver implementation parses it as a string. We need some tests and fixes around this.

Reported-by: @rbdr

ws test failing on macos

Brief bug report because I'm on mobile walking home. Spent a few hours today debugging why the Travis tests on MacOS are failing and I'm very confused. I'm seeing this on the 'main' branch and also on my branch with the new Pull-WS.

We're starting a WS client and server, and then connecting to server.stringify(), but when we do the if (err) throw err dance we end up throwing a weird error.

The error originates from a net server on some ungodly high port, but plugs.js isn't listening there. Maybe the problem is caused by multiple tests running at the same time? Anyway, the problem is that we're receiving a TCP + SHS connection but the client connecting doesn't have a public key in their address. I don't know what would cause this.

Tomorrow I'll probably edit the test command so that we're only testing the one file. I'm worriee about why we'd be propagating an error from another server (race condition?), but I wouldn't be surprised.

Two ssb-servers with plugin/unix-socket cannot run at the same time

Commit faa9526 introduced a module-level variable started (l.6-l.7) that is used to avoid reinitializing the unix socket twice. Unfortunately, when the plugin is used in two different Secret-Stacks running within the same node process, e.g. in unit tests, this strategy prevents the second instance from being initialized properly and is therefore ignored.

Moreover, l.27 returns undefined and therefore upon closing, the second instance fails with the following error:

.../node_modules/multiserver/compose.js:33
    if (f.length) return f(cb)
          ^

TypeError: Cannot read property 'length' of undefined
    at fnAsAsync (.../node_modules/multiserver/compose.js:33:11)
    at closeMultiserverServer (.../node_modules/multiserver/index.js:57:37)
    at close (.../node_modules/secret-stack/lib/core.js:259:82)
    at hooked (.../node_modules/hoox/index.js:10:15)
    at .../node_modules/ssb-db/index.js:90:11
    at .../node_modules/flumedb/index.js:263:25
    at .../node_modules/continuable-hash/index.js:25:25
    at Single._written (.../node_modules/async-single/proto.js:46:12)
    at release (.../node_modules/mutexify/index.js:25:13)
    at .../node_modules/atomic-file/inject.js:27:11
npm ERR! Test failed.  See above for more details.

At least, l.27 should be fixed to avoid an error on closing. Better, the check for avoiding duplicate initialization should be specific to a specific instance of Secret-Stack and not module-wide (which is initialized only once for all node module imports).

@arj03

Unix socket with default path exposes username over network

I saw in %94G9B4MPIbhQynNlCApdn88PlT73/PE9l/jKmOsxy84=.sha256 that we're broadcasting the unix socket over UDP, which exposes our username to all network peers. I'd love to avoid this, if possible. Potential solutions:

  1. Don't broadcast unix sockets (?)

  2. Use a temporary socket with mktemp

    $ mktemp
    /tmp/tmp.NrzUtkX6jl

remove footguns, make it hard to create insecure configurations

Today I gave a talk on designing for security. One of my main messages was "don't make it configurable" or at least "don't make it easier to have an insecure configuration"

I remembered that multiserver noauth is a potential problem here.
It would be easy to set net~noauth and it would be faster! except now anyone can connect to you, and tell you what to do, if you have noauth set.

It's not a responsible design to expect them to understand not to use noauth with a network.
They should have to explicitly enable the footgun.

My idea to approach this: first make sure all the plugins set an address property consistently.
then, have noauth take an address config option, and refuse connections that do not have the correct address. This means to enable noauth, you have to set the address twice!

ws: [{scope: 'device', port: 8989, transform: { name: 'noauth', address: 'ws://localhost:8989' }}]

or something like that. We don't have a way to specify configuration for transforms yet.

I have this about half coded

thoughts? @arj03 @regular

Recent versions of pull-stream break WebSocket plugin

It looks like require('pull-stream/pull') no longer has map, but require('pull-stream') does.

/node_modules/multiserver/plugins/ws.js:34
          stream.source = pull(stream.source, pull.map(Buffer))
                                                   ^

TypeError: pull.map is not a function
    at onConnect (/node_modules/multiserver/plugins/ws.js:34:52)
    at WebSocket.<anonymous> (/node_modules/pull-ws/source.js:49:13)
    at WebSocket.onError (/node_modules/ws/lib/WebSocket.js:452:14)
    at emitOne (events.js:116:13)
    at WebSocket.emit (events.js:211:7)
    at ClientRequest.onerror (/node_modules/ws/lib/WebSocket.js:711:10)
    at emitOne (events.js:116:13)
    at ClientRequest.emit (events.js:211:7)
    at Socket.socketErrorListener (_http_client.js:387:9)
    at emitOne (events.js:116:13)

scopes

first some context, (maybe this can become documentation?)

multiserver is designed around plugins. there are two types of plugins, transports and transforms. Then transports make a connection to something, and transforms change something about it, such as encrypting and authenticating it, or compressing it. so far, we've just used the shs transform plugin to encrypt everything.

multiserver gives you a convienent handle to run multiple servers, with multiple protocol versions at the same time. that's why it's called multiserver. So, you can have multiple servers. We started to add that, but then we realized we needed something more: scopes. servers are useful for lots of things, but sometimes you don't want everyone to connect to them (we care about privacy, and would rather someone connect via tor, but local clients can use net), and other times, you know that someone can't connect to them anyway, so we want to avoid telling them about it. (we are running our server on a laptop on wifi, without a stable ip address. so we don't want to advertise our address publically, but maybe we do for another multiserver that is running on cjdns or tor etc)

Each transport plugin now takes a scope option, which is a string: the name of that scope. when a plugin is created, the plugin is passed the scope it will have. eg, Net({host: ..., port: ..., scope: 'public'})
plugins also have a scope() method, which just returns the scope it was configured with, or defaults to. so Plugin({scope: X}).scope() === X

When you call multiserver.stringify(scope) it then returns an address for that scope.
If scope is "private" then public plugins are allowed.

Okay I think I understand all that now. but why isn't my websocket server attaching? hmm...

How should the `server` option be used?

It looks like we're having problems that seem to be resolved by server: true. From skimming the source, I can see opts.server used in three places:

$ git grep opts.server
plugins/onion.js:      if(!opts.server) return
plugins/onion.js:      if(opts && !opts.server) return null
plugins/unix-socket.js:    server: !opts.server ? null : function (onConnection) {
plugins/unix-socket.js:      if(opts && !opts.server) return null
plugins/ws.js:  var secure = opts.server && !!opts.server.key
plugins/ws.js:      var server = opts.server || http.createServer(opts.handler)
plugins/ws.js:      if(!opts.server) {
plugins/ws.js:      if(opts.server)
plugins/ws.js:        port = opts.server.address().port
  • Tor: when opts.server is falsy, it looks like the connection is abandoned and we instead just return.
  • Unix socket: Same as above, but instead of a function that immediately returns the function is being replaced by null. Is there a reason for the difference in these two implementations?
  • Websocket: It looks like opts.server is an object with two expected properties: key and address, and the latter is a function that returns an object with the property port. The full usage is opts.server.address().port, which makes me think there are some moving parts here I don't understand.

@dominictarr Could you add some info to help me better understand what we should be doing with this option? Apologies if this has already been recorded somewhere, so far I've checked #32, ssb-config, and scuttlebot but didn't see any notes about this change. Appreciate your help!

cc: @arj03 @cryptix

net throws, when used in browser

in the browser, multiserver should not use net, hopefully they have also provided an address that works in the browser, such as websockets.

Stringify may be returning the wrong address for net and ws

See: ssbc/patchwork#1035

We have a scenario where config.connections.incoming is specifying the correct IP addresses but it looks like this code is still falling back to multiserver-scopes (i.e. non-private-ip) in the local scope:

var _host = (scope == 'public' && opts.external) || scopes.host(scope)

I'd imagine that this is a bug or maybe I'm completely misunderstanding what this is supposed to do? For example, my config:

{ net:
  [ { host: '127.0.0.1',
    port: 8008,
    scope: [ 'device' ],
    transform: 'shs' },
    { host: '::1', port: 8008, scope: [ 'device' ], transform: 'shs' },
    { host: '192.168.0.109',
      port: 8008,
      scope: [ 'device', 'local', 'public' ],
      transform: 'shs' },
    { host: '172.18.0.1',
      port: 8008,
      scope: [ 'device', 'local', 'public' ],
      transform: 'shs' },
    { host: 'fce2:9811:4862:81a7:bb08:91d6:2e41:d220',
      port: 8008,
      scope: [ 'device', 'local', 'public' ],
      transform: 'shs' } ],
  ws:
  [ { host: '127.0.0.1',
    port: 8989,
    scope: [ 'device' ],
    transform: 'shs' },
    { host: '::1', port: 8989, scope: [ 'device' ], transform: 'shs' },
    { host: '192.168.0.109',
      port: 8989,
      scope: [ 'device', 'local', 'public' ],
      transform: 'shs' },
    { host: '172.18.0.1',
      port: 8989,
      scope: [ 'device', 'local', 'public' ],
      transform: 'shs' },
    { host: 'fce2:9811:4862:81a7:bb08:91d6:2e41:d220',
      port: 8989,
      scope: [ 'device', 'local', 'public' ],
      transform: 'shs' } ],
  unix: [ { scope: 'device', transform: 'noauth' } ] } }

But over the network I see:

net:192.168.0.109:8008~shs:+oaWWDs8g73EZFUMfW37R/ULtFEjwKN/DczvdYihjbU=;net:192.168.0.109:8008~shs:+oaWWDs8g73EZFUMfW37R/ULtFEjwKN/DczvdYihjbU=;net:192.168.0.109:8008~shs:+oaWWDs8g73EZFUMfW37R/ULtFEjwKN/DczvdYihjbU=;ws://192.168.0.109:8989~shs:+oaWWDs8g73EZFUMfW37R/ULtFEjwKN/DczvdYihjbU=;ws://192.168.0.109:8989~shs:+oaWWDs8g73EZFUMfW37R/ULtFEjwKN/DczvdYihjbU=;ws://192.168.0.109:8989~shs:+oaWWDs8g73EZFUMfW37R/ULtFEjwKN/DczvdYihjbU=

Replacing the semicolons with newlines (I'd love if this was the default):

net:192.168.0.109:8008~shs:+oaWWDs8g73EZFUMfW37R/ULtFEjwKN/DczvdYihjbU=
net:192.168.0.109:8008~shs:+oaWWDs8g73EZFUMfW37R/ULtFEjwKN/DczvdYihjbU=
net:192.168.0.109:8008~shs:+oaWWDs8g73EZFUMfW37R/ULtFEjwKN/DczvdYihjbU=
ws://192.168.0.109:8989~shs:+oaWWDs8g73EZFUMfW37R/ULtFEjwKN/DczvdYihjbU=
ws://192.168.0.109:8989~shs:+oaWWDs8g73EZFUMfW37R/ULtFEjwKN/DczvdYihjbU=
ws://192.168.0.109:8989~shs:+oaWWDs8g73EZFUMfW37R/ULtFEjwKN/DczvdYihjbU=

Maybe relevant: #22

cc: @dominictarr @arj03 @regular @cryptix

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.