ssbc / muxrpc

lightweight multiplexed rpc

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Connection handshake

pfrazee opened this issue · comments

Long run, I think we're going to need to add a handshake to the rpc apis that establishes protocol and API compatibility, particularly RE: versions. This may not be part of muxrpc, but I thought it best to file the issue here.

It may make sense to implement this as an API call on top of muxrpc, eg handshake(). That would enable the APIs to define their own handshakes.

The thing here is that auth needs to be set up before the api is attached to the stream. it's also permissions - what api you are willing to give is dependent on who they are.

something like

rpc.auth(credentals, function (err) {
  if(err) throw err ///not authed
  rpc.doStuff(...)
})

Can we combine auth and api negotiation so it's just one opening message?

...almost. to do the auth securely, we'd need to sign a challenge sent by the remote.
If it's inside of a private-stream we can use the hash of the secret as the challenge.
The important thing is that the server can trust the challenge (i.e. because they got to choose it)
which prevents replay attacks without having to store previous authorizations.

maybe a better way to do this would in a separate stream handshake before the api starts?

I'm good with anything so long as it can communicate API metadata (such as versioning) without too much cost.

yup, sure thing.

To establish compatible protocols, we essentially need an RPC require call that includes versioning info. Something like:

var rpc = muxrpc(commonBase, commonBase)(...)
rpc.declare('ssb-replication@1.0.0', { source: ['createHistoryStream'] })
rpc.provide('ssb-replication@1.0.0', {
  createHistoryStream: function() { ... }
})
rpc.require('ssb-replication@~1.0.0', function(err) {
  if (err) throw "Peer is not compatible"
  rpc.createHistoryStream() //...
})

oh hey this is a pretty good idea! each thing would add some set of apis, and the version for that set would be passed during the auth handshake. as in: #7

You remember our conversation about plan9's config which mapped names to endpoints at runtime? Is that something we want to consider here?

An rpc.require() call asks the other end to load a specific API for them. The rpc.provide() call pre-configures an API load, in case the other end asks for it. If we're doing routing, then we'll need logic on the provide call. Maybe something like this:

rpc.provide('ssb-replication@1.0.0', function(cb) {
  // load balancer: look up the shard that hosts this user
  var node = custer.lookupShard(rpc.ident)
  cb(null, node.connect()) // pass the `require` call to a downstream RPC 
})

hmm. this feels like getting ahead of ourselves. What is a near term thing we could use this for?

We don't need it near term.