1 | # multiserver
|
2 |
|
3 | A single interface that can work with multiple protocols,
|
4 | and multiple transforms of those protocols (eg, security layer)
|
5 |
|
6 | ## address format
|
7 |
|
8 | Addresses describe everything needed to connect to a peer.
|
9 | each address is divided into protocol sections separated by `~`.
|
10 | Each protocol section is divided itself by `:`. A protocol section
|
11 | starts with a name for that protocol, and then whatever arguments
|
12 | that protocol needs.
|
13 |
|
14 | For example, the address for my ssb pubserver is:
|
15 | ```
|
16 | net:wx.larpa.net:8008~shs:DTNmX+4SjsgZ7xyDh5xxmNtFqa6pWi5Qtw7cE8aR9TQ=
|
17 | ```
|
18 | That says use the `net` protocol (TCP) to connect to the domain `wx.larpa.net`
|
19 | on port `8008`, and then encrypt the session using `shs` ([secret-handshake](https://github.com/auditdrivencrypto/secret-handshake))
|
20 | to the public key `DTNmX+4SjsgZ7xyDh5xxmNtFqa6pWi5Qtw7cE8aR9TQ=`.
|
21 |
|
22 | Usually, the first section is a network protocol, and the rest are transforms,
|
23 | such as encryption or compression.
|
24 |
|
25 | Multiserver makes it easy to use multiple protocols at once. For example,
|
26 | my pub server _also_ supports `shs` over websockets.
|
27 |
|
28 | So, this is another way to connect:
|
29 |
|
30 | ```
|
31 | wss://wx.larpa.net~shs:DTNmX+4SjsgZ7xyDh5xxmNtFqa6pWi5Qtw7cE8aR9TQ=
|
32 | ```
|
33 |
|
34 | if your server supports multiple protocols, you can concatenate addresses with `;`
|
35 | and multiserver will connect to the first address it understands.
|
36 |
|
37 | ```
|
38 | net:wx.larpa.net:8008~shs:DTNmX+4SjsgZ7xyDh5xxmNtFqa6pWi5Qtw7cE8aR9TQ=;wss://wx.larpa.net~shs:DTNmX+4SjsgZ7xyDh5xxmNtFqa6pWi5Qtw7cE8aR9TQ=
|
39 | ```
|
40 | This means use net, or wss. In some contexts, you might have a peer that understands
|
41 | websockets but not net (for example a browser), as long as a server speaks at least
|
42 | one protocol that a peer can understand, then they can communicate.
|
43 |
|
44 | ### net
|
45 |
|
46 | TCP is a `net:{host}:{port}` port is not optional.
|
47 |
|
48 | ### ws
|
49 |
|
50 | WebSockets `ws://{host}:{port}?` port defaults to 80 if not provided.
|
51 |
|
52 | WebSockets over https is `wss://{host}:{port}?` where port is
|
53 | 443 if not provided.
|
54 |
|
55 | ### onion
|
56 |
|
57 | Connect over tor using local proxy (9050). Onion is `onion:{host}:{port}` port is not optional.
|
58 |
|
59 | ### reactnative-channel
|
60 |
|
61 | The [multiserver-rn-channel](http://npm.im/multiserver-rn-channel) module implementes
|
62 | a multiserver protocol for use inbetween the reactnative nodejs process and browser process.
|
63 |
|
64 | ### shs
|
65 |
|
66 | Secret-handshake is `shs:{public_key}:{seed}?`. `seed` is used to create
|
67 | a one-time shared private key, that may enable a special access.
|
68 | For example, you'll see that ssb invite codes have shs with two sections
|
69 | following. Normally, only a single argument (the remote public key) is necessary.
|
70 |
|
71 | ### combined
|
72 |
|
73 | a network protocol is combined with 1 or more transform protocols,
|
74 | for example: `net:{host}:{port}~shs:{key}`
|
75 |
|
76 | ### multi
|
77 |
|
78 | A server that runs multiple protocols on different ports can simply join them
|
79 | with `;` and clients should connect to their preferred protocol.
|
80 | clients may try multiple protocols on the same server before giving up,
|
81 | but generally it's unlikely that protocols should not fail independently
|
82 | (unless there is a bug in one protocol).
|
83 |
|
84 | an example of a valid multiprotocol:
|
85 | `net:{host}:{port}~shs:{key};ws:{host}:{port}~shs:{key}`
|
86 |
|
87 |
|
88 |
|
89 | ### TODO
|
90 |
|
91 | A short list of other protocols which could be implemented:
|
92 |
|
93 | * cjdns
|
94 | * other encryption protocols...
|
95 |
|
96 | ## motivation
|
97 |
|
98 | Developing a p2p system is hard. especially hard is upgrading protocol layers.
|
99 | The contemporary approach is to [update code via a backdoor](https://whispersystems.org/blog/the-ecosystem-is-moving/),
|
100 | but as easily as security can be added, it can be taken away. We need an approach
|
101 | to upgrading that is itself decentralized, and also does not accumulate legacy baggage.
|
102 | after upgrading past a version of the protocol, the system should be able to discard that
|
103 | without a trace.
|
104 |
|
105 | Traditionally, protocol versions are upgraded by negioating the version used in a handshake.
|
106 | But, how do you upgrade the handshake? You can't. This also tends to accumulate legacy, because
|
107 | you never know if you'll meet an old peer.
|
108 |
|
109 | Some HTTP APIs provide upgradability a better, simpler way.
|
110 | By putting a version number within the url. A new version of
|
111 | the API can then be used without touching the old one at all.
|
112 |
|
113 | I propose to adapt this approach to lower level protocols.
|
114 | Do not negioate versions/ciphersuits in the handshake.
|
115 | Instead, run multiple protocols at once, and "lookup" which
|
116 | versions a peer supports currently. Most p2p systems have
|
117 | some sort of lookup system to find peers _anyway_
|
118 | (might be DHT, a tracker server, or gossip),
|
119 | just put version information in there.
|
120 |
|
121 | There are two main situations where I expect this to be useful:
|
122 | upgrading ciphers and bridging across enviroments that are
|
123 | otherwise cannot talk to each other (web browser to desktop)
|
124 |
|
125 | ### upgrade
|
126 |
|
127 | If a peer wants to upgrade from *weak* protocol
|
128 | to a *strong* one, they simply start serving *strong* via another port,
|
129 | and advertise that in the lookup system.
|
130 | Now peers that have support for *strong* can connect via that protocol.
|
131 |
|
132 | Once most peers have upgraded to strong, support for *weak* can be discontinued.
|
133 |
|
134 | ### bridging
|
135 |
|
136 | Regular servers can do TCP. Desktop clients can speak TCP,
|
137 | but can't create TCP servers reliably. Browsers can
|
138 | use WebSockets and WebRTC. WebRTC gives you p2p, but
|
139 | needs an introducer. Another option is [utp](https://github.com/mafintosh/utp-native)
|
140 | - probably the most convienent, because it doesn't need an introducer
|
141 | on _every connection_ (but it does require some bootstrapping),
|
142 | but that doesn't work in the browser either.
|
143 |
|
144 | ``` js
|
145 | var MultiServer = require('multiserver')
|
146 |
|
147 | var ms = MultiServer([
|
148 | require('multiserver/plugins/net')({port: 1234}),
|
149 | require('multiserver/plugins/ws')({port: 2345})
|
150 | ])
|
151 |
|
152 | //start a server (for both protocols!)
|
153 | //returns function to close the server.
|
154 | var close = ms.server(function (stream) {
|
155 | //handle incoming connection
|
156 | })
|
157 |
|
158 | //connect to a protocol. uses whichever
|
159 | //handler understands the address (in this case, websockets)
|
160 | var abort = ms.client('ws://localhost:1234', function (err, stream) {
|
161 | //...
|
162 | })
|
163 |
|
164 | //at any time abort() can be called to cancel the connection attempt.
|
165 | //if it's called after the connection is established, it will
|
166 | //abort the stream.
|
167 | ```
|
168 |
|
169 | ### example - server with two security protocols
|
170 |
|
171 | This is just how some services (eg, github) have an API version
|
172 | in their URL scheme. It is now easy to use two different
|
173 | versions in parallel. later, they can close down the old API.
|
174 | ``` js
|
175 | var MultiServer = require('multiserver')
|
176 | var ms = MultiServer([
|
177 | [ //net + secret-handshake
|
178 | require('multiserver/plugins/net')({port: 3333}),
|
179 | require('secret-handshake-multiserver')({
|
180 | keys: //keypair
|
181 | appKey: //application key
|
182 | auth: //auth function (only needed for server)
|
183 | }),
|
184 | ],
|
185 | [ //net + secret-handshake2
|
186 | //(not implemented yet, but incompatible with shs)
|
187 | require('multiserver/plugins/net')({port: 4444}),
|
188 | require('secret-handshake2-multiserver')({
|
189 | keys: //keypair
|
190 | appKey: //application key
|
191 | auth: //auth function (only needed for server)
|
192 | }),
|
193 | ]
|
194 | ]
|
195 |
|
196 | console.log(ms.stringify())
|
197 |
|
198 | //=> net:<host>:3333~shs:<key>;net:<host>:4444~shs2:<key>
|
199 |
|
200 | //run two servers on two ports.
|
201 | //newer peers can connect directly to 4444 and use shs2.
|
202 | //this means the protocol can be _completely_ upgraded.
|
203 | ms.server(function (stream) {
|
204 |
|
205 | })
|
206 |
|
207 | //connect to legacy protocol
|
208 | ms.client('net:<host>:3333~shs:<key>', function (err, stream) {
|
209 | //...
|
210 | })
|
211 |
|
212 | //connect to modern protocol
|
213 | ms.client('net:<host>:4444~shs2:<key>', function (err, stream) {
|
214 | //...
|
215 | })
|
216 |
|
217 | ```
|
218 |
|
219 | ## License
|
220 |
|
221 | MIT
|
222 |
|