ssbc/ssb-cli-dashboard

Name: ssb-cli-dashboard

Owner: Secure Scuttlebutt Consortium

Description: A cli dashboard for scuttlebot

Created: 2015-09-18 23:59:11.0

Updated: 2018-03-27 03:13:57.0

Pushed: 2016-03-17 02:25:21.0

Homepage: null

Size: 3602

Language: JavaScript

GitHub Committers

UserMost Recent Commit# Commits

Other Committers

UserEmailMost Recent Commit# Commits

README

SSB CLI Dashboard

clone https://github.com/ssbc/ssb-cli-dashboard.git
sb-cli-dashboard
install

Then:

st all feeds
feeds.js

ew the given feed
feed.js {feedid}   

ew blobs linked-to by the given feed
blobs.js {feedid}

ew feeds related to the given feed
graph.js [follows|followers|flags|flaggers] {feedid}

twork status
gossip.js

./demo.gif

How it works

The feeds.js view pulls some metadata from Scuttlebot, and uses that to produce a master list of known feeds.

oad data from sbot
done = multicb({ pluck: 1, spread: true })

(
ot.latest(),                          // get the sequence number of the latest message of each known feed
ilter) ? pull.filter(filter) : null,  // apply a filter, if given
ll.collect(done())                    // collect into an array

.friends.all('follow', done())        // fetch the computed follow-graph
.friends.all('flag', done())          // fetch the computed flag-graph

(function (err, feeds, follows, flags) {
 ...

Each entry shows:

id [seq: N follows: N/N flags: N/N]

Where, in the case of follows and flags, it's showing the outbound then inbound. It computes this info by counting directed-edges:

elper to count how many nodes in the graph have edges pointing to the given ID
 graphs are given in the shape of { sourceIds: { destIds: true } }
 eg. if bob follows alice, the follow graph would include { bobsId: { alicesId: true } }
 if alice also followed bob, the follow graph would be { bobsId: { alicesId: true }, alicesId: { bobsId: true } }
tion countInbounds (graph, id) {
r n = 0
r (var id2 in graph)
if (graph[id2][id])
  n++
turn n


elper to count how many nodes in the graph the given ID points to
 see `countInbounds` comment for more info
tion countOutbounds(graph, id) {
turn Object.keys(graph[id] || {}).length


ake the feeds and graphs, produce textual list items
tion feedsToListItems (feeds, follows, flags) {
 sort by highest sequence to lowest sequence
eds.sort(function (a, b) {
return b.sequence - a.sequence


 produce a list of labels
turn feeds.map(function (f) {
var info = [
  'seq: '     + f.sequence,
  'follows: ' + countOutbounds(follows, f.id) + '/' + countInbounds(follows, f.id),
  'flags: '   + countOutbounds(flags, f.id)   + '/' + countInbounds(flags, f.id)
]
return f.id + '[' + info.join(' ') + ']'


In feed.js, the list is a simple stream-fetch:

(sbot.createUserStream({ id: userId }), pull.collect(function (err, log) {
 ...

graph.js uses the same code in feeds.js, but it applies a filter. It generates the filter using this function:

tion filteredFeeds (graph, inbound, label) {
r included = {}
ot.friends.all(graph, function (err, g) {
if (inbound) {
  // collect feeds with an edge to `userId`
  for (var id2 in g)
    if (g[id2][userId])
      included[id2] = true
} else {
  // use the already-computed `userId` edges
  included = g[userId] || {}
}

nction filter (entry) {
return included[entry.id]

 ...

Here's how it's applied to create the four graph-filters:

graphs = {
ollows':   filteredFeeds('follow', false, 'Follows'),
ollowers': filteredFeeds('follow', true, 'Followers'),
lags':     filteredFeeds('flag', false, 'Flags'),
laggers':  filteredFeeds('flag', true, 'Flaggers'),

blobs.js collects every reference to a blob made by the given userId, and lists them in the form:

id (N references)

It does this by first searching for any messages by userId that link to a blob. Then, it groups the messages by blob, producing the final list.

blobs, blobMessageMap = {}
(
 fetch messages by `userId` which link to a blob
ot.links({ source: userId, dest: '&', values: true }),

 group together messages that publish a blob
ll.filter(function (index) {
var blobId = index.dest
if (!blobMessageMap[blobId]) {
  blobMessageMap[blobId] = [index]
  return true
}
blobMessageMap[blobId].push(index)
return false
,

 collect into an array
ll.collect(function (err, _blobs) {
if (err) throw err
blobs = _blobs

// sort by the number of references to the blob
blobs.sort(function (a, b) {
  return blobMessageMap[b.dest].length - blobMessageMap[a.dest].length
})

// render in the list widget
var listItems = blobs.map(function (index) { 
  return index.dest + ' ('+blobMessageMap[index.dest].length+' references)'
})
listWidget.setItems(listItems)
listWidget.select(0)
screen.render()


Finally, gossip.js simply polls the status of the gossip-network and renders it periodically.

tion poll () {
ot.gossip.peers(function (err, peers) {
if (err) throw err
table.setData(peersToTableData(peers))
screen.render()



()
nterval(poll, 1000)

tion status (peer) {
 (peer.connected)
return 'Connected'
 (peer.time && peer.time.connect > peer.time.attempt)
return 'Connecting'
 (peer.failure)
return peer.failure + ' Failures'
turn 'Disconnected'


tion peersToTableData (peers) {
ers.sort(function (a, b) {
var an = (a.announcers) ? a.announcers.length : 0
var bn = (b.announcers) ? b.announcers.length : 0
return bn - an


turn {
headers: ['Announcers', 'Address', 'Status'],
data: peers.map(function (p) {
  return [
    (p.announcers) ? p.announcers.length : 0,
    p.host + ':' + p.port + ':' + p.key,
    status(p)
  ]
})



This work is supported by the National Institutes of Health's National Center for Advancing Translational Sciences, Grant Number U24TR002306. This work is solely the responsibility of the creators and does not necessarily represent the official views of the National Institutes of Health.