Skip to content

Commit e3eaa60

Browse files
committed
Update README
1 parent 90d8959 commit e3eaa60

File tree

3 files changed

+80
-46
lines changed

3 files changed

+80
-46
lines changed

README.md

+50-45
Original file line numberDiff line numberDiff line change
@@ -18,16 +18,17 @@ protocol to share dispatched redux actions among peers, and eventually agree on
1818
their order in time. As actions from the past arrive, we replay history as if
1919
they had always existed.
2020

21-
A sample "server" peer is included, which might sync changes to a database,
22-
write a persistent log, or manage system/world/NPC actors.
21+
A sample "server" peer is included, which might sync state changes to a
22+
database, write a persistent log, or manage system/world/NPC actors.
2323

24-
While it works great in a client-server set up, you could upgrade/downgrade to
25-
peer-to-peer connections, or go offline, and changes sync when you next connect.
24+
While it works great in a traditional client-server set up, you can flexibly
25+
upgrade/downgrade to peer-to-peer connections, go offline for minutes or days,
26+
and changes will sync when you next connect to another scuttlebutt instance.
2627

27-
Note, scuttlebutt does not make any guarantees of security or identity. Peer
28-
`Bob` is free to lie to `Jane` about `Amy`'s actions. A client-server layout can
29-
mitigate this risk, and WebSockets over SSL mitigates MITM replacement, but as
30-
the project matures this will be brought into consideration.
28+
Note, by default, scuttlebutt itself does not make any guarantees of security or
29+
identity: peer `Bob` is able to lie to `Jane` about `Amy`'s actions. Security
30+
guarantees can added using the
31+
[`signAsync` and `verifyAsync`](#signasync--verifyasync)] dispatcher options.
3132

3233
For more, read the
3334
[Scuttlebutt paper](http://www.cs.cornell.edu/home/rvr/papers/flowgossip.pdf).
@@ -55,15 +56,14 @@ export default (initialState) => {
5556
uri: 'http://localhost:3000',
5657
}))
5758
}
58-
5959
```
6060

6161
It wraps your store's root reducer (to store history), `getState` (to return the
6262
current state in history) and `dispatch` (to connect to peers).
6363

64-
If you're using the redux devtools enhancer, it must come *after* the redux-
65-
scuttlebutt enhancer (or scuttlebutt will try to emit `PERFORM_ACTION` across
66-
the network)
64+
If you're using the redux dev-tools enhancer, it must come *after* the redux-
65+
scuttlebutt enhancer (or scuttlebutt will emit `PERFORM_ACTION` actions over the
66+
network).
6767

6868
## options
6969

@@ -115,33 +115,29 @@ scuttlebutt({
115115
})
116116
```
117117

118-
### verifyAsync & signAsync
118+
### signAsync & verifyAsync
119+
120+
The dispatcher options `signAsync` and `verifyAsync` allows you to add arbitrary
121+
metadata to actions as they are dispatched, and filter remote actions which are
122+
received from peers. This means you can validate any action against itself or
123+
the redux state, other actions in history, a cryptographic signature, rate
124+
limit, or any arbitrary rule.
119125

120-
The dispatcher option `verifyAsync` allows you to filter remote actions
121-
dispatched or gossiped about through scuttlebutt. You could validate an action's
122-
contents against a cryptographic signature, or rate limit, or an arbitrary rule:
126+
For security, you can use
127+
[redux-signatures](https://github.com/grrowl/redux-signatures) to add Ed25519
128+
signatures to your actions. This could be used to
129+
verify authors in a peering or mesh structure.
123130

124131
```js
125-
import { UPDATE_ACTION } from 'redux-scuttlebutt'
126-
127-
function verifyAsync(callback, action, getStateHistory) {
128-
const history = getStateHistory(),
129-
prevUpdate = history[history.length - 1],
130-
prevAction = prevUpdate && prevUpdate[UPDATE_ACTION]
131-
132-
if (
133-
// if this message doesn't include an e
134-
action && action.payload
135-
&& action.payload.indexOf('e') === -1
136-
// and the previously message didn't include an e
137-
&& prevAction && prevAction && prevAction.payload
138-
&& prevAction.payload.indexOf('e') === -1
139-
) {
140-
callback(false)
141-
} else {
142-
callback(true)
143-
}
144-
}
132+
import { Ed25519, verifyAction, signAction } from 'redux-signatures'
133+
134+
const identity = new Ed25519()
135+
136+
scuttlebutt({
137+
uri: 'http://localhost:3000',
138+
signAsync: signAction.bind(this, identity),
139+
verifyAsync: verifyAction.bind(this, identity),
140+
}))
145141
```
146142

147143
The `getStateHistory` parameter returns an array of the form
@@ -177,11 +173,6 @@ strategies may be,
177173

178174
Examples are found under `examples/`.
179175

180-
<!--
181-
You may have to `npm link` your redux-scuttlebutt directory and `npm link redux-
182-
scuttlebutt` your example project directory during development.
183-
-->
184-
185176
* `counter`:
186177
[redux counter example](https://github.com/reactjs/redux/tree/master/examples/counter)
187178
with the addition of redux-scuttlebutt.
@@ -197,16 +188,27 @@ scuttlebutt` your example project directory during development.
197188
tradeoffs, which we don't want to bake into the library itself.
198189
* our recommendation is to implement what's right for your implementation in
199190
userland.
200-
* i've released an example of message signing with ed25519 signatures and
191+
* have released an example of message signing with ed25519 signatures and
201192
asyncronous message validation
202193
[in this gist](https://gist.github.com/grrowl/ca94e47a6da2062e9bd6dad211588597).
203-
* some comments on our underlying `scuttlebutt` implementation
194+
* released [redux-signatures](https://github.com/grrowl/redux-signatures)
195+
which plugs directly into the dispatcher.
196+
* Allows flexible implementation, e.g. in a client-server topology you may
197+
only want to use `sign` on the client and `verify` on the server only.
198+
This avoids running the most processor intensive part on the clients with
199+
no loss of security.
200+
* underlying `scuttlebutt` implementation
201+
* currently depends on our
202+
[own scuttlebutt fork](https://github.com/grrowl/scuttlebutt#logical-timestamps),
203+
not yet published to npm, I'm not sure if dominictarr wants to accept these
204+
changes upstream.
205+
* should probably republish as `scuttlebutt-logical`
204206
* add a `@@scuttlebutt/COMPACTION` action
205-
* reducers would recieve the whole history array as `state`
207+
* reducers would receive the whole history array as `state`
206208
* enables removing multiple actions from history which are inconsequential —
207209
such as multiple "SET_VALUE" actions, when only the last one applies.
208210
* also enables forgetting, and therefore not replaying to other clients,
209-
actions after a certain threshold
211+
actions after a certain threshold.
210212
* implement CRDT helpers for reducers to easily implement complex shared data
211213
types.
212214
* tests
@@ -215,6 +217,9 @@ scuttlebutt` your example project directory during development.
215217
* ensure API
216218
* allow pluggable socket library/transport
217219
* more example applications! something real-time, event driven.
220+
* WebRTC support
221+
* Genericize server into websockets and webrtc versions
222+
* Write client modules to support either
218223

219224
## contributions
220225

THOUGHTS.md

+29
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,35 @@ in the spirit of open source, here's some related thoughts. (newest first)
22

33
----
44

5+
## verifyAsync against any arbitrary rule
6+
7+
would be cool to have examples like "everyone can only use each letter once",
8+
although it has race conditions involved. maybe thats a good thing for example?
9+
although in real life your stores will diverge :O
10+
11+
```js
12+
import { UPDATE_ACTION } from 'redux-scuttlebutt'
13+
14+
function verifyAsync(callback, action, getStateHistory) {
15+
const history = getStateHistory(),
16+
prevUpdate = history[history.length - 1],
17+
prevAction = prevUpdate && prevUpdate[UPDATE_ACTION]
18+
19+
if (
20+
// if this message doesn't include an e
21+
action && action.payload
22+
&& action.payload.indexOf('e') === -1
23+
// and the previously message didn't include an e
24+
&& prevAction && prevAction && prevAction.payload
25+
&& prevAction.payload.indexOf('e') === -1
26+
) {
27+
callback(false)
28+
} else {
29+
callback(true)
30+
}
31+
}
32+
```
33+
534
## prior art
635

736
Most of my research has been on the technical side, reading papers and looking

examples/chat/index.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ const devToolsConfig = {
1212
const enhancer = compose(
1313
applyMiddleware(scuttlebutt),
1414
window.__REDUX_DEVTOOLS_EXTENSION__ ?
15-
window.__REDUX_DEVTOOLS_EXTENSION__(devToolsConfig) : f => f,
15+
window.__REDUX_DEVTOOLS_EXTENSION__(devToolsConfig) : f => f
1616
)
1717

1818
const store = createStore(counter, undefined, enhancer)

0 commit comments

Comments
 (0)