Migrate from gossip to ssb-standard-conn

TO DO
  • ssb-legacy-conn: replace utils::find with Array.find
  • ssb-legacy-conn: use TypeScript
  • create ssb-conn-db package
  • ssb-legacy-conn: use ssb-conn-db instead of atomic-file
  • ssb-legacy-conn: make sure local peers are not stored in conn-db
  • create ssb-conn-hub package
  • ssb-legacy-conn: use ssb-conn-hub
  • ssb-legacy-conn: get rid of peers, instead cross conn-db data with conn-hub data
  • ssb-conn-hub: add debug() logs
  • ssb-conn-staging: create this
  • ssb-conn-db: add debug() logs
  • ssb-legacy-conn: match ssb-gossip 1.0.10
  • Consider Aljoscha's thoughts: %h7rjRPQ8KBhm7k3BGoK2TV6eIraVBiUnG5ecSvL4HEk=.sha256 (peer sampling service)
  • ssb-legacy-conn and conn-hub: make ConnHub.close() that shuts things down
  • ssb-legacy-conn: extract init.js and schedule.js further above in the abstraction hierarchy
  • ssb-conn-query: refactor this out of gossip::schedule.js
  • ssb-legacy-conn: use ssb-conn-query
  • ssb-conn-db: should record birth field
  • ssb-conn-hub: live pull-stream for entries()
  • ssb-conn-staging: live pull-stream for entries()
  • ssb-conn: register plugin gossip as an alias for conn and implement at least .gossip.ping for anonymous remote peers
  • create ssb-conn within ssb-legacy-conn, rearchitect
  • Manyverse: rearchitect all 'peer' metadata to be [string, data], starting from SSB driver
  • Manyverse: make sure LAN peers work as expected
  • Manyverse: make sure Bluetooth peers work as expected
  • UX Manyverse: peers 'connecting' look different than peers 'connected'
  • Manyverse: make sure pubs work as expected
  • ssb-conn-staging: add field {updated: timestamp} to all KVs
  • ssb-conn-staging: when connecting OR connected, unstage
  • UX Manyverse: selecting a connected peer opens a popup (open profile, disconnect)
  • UX Manyverse: selecting a staged peer opens a popup (open profile, connect, connect-then-follow)
  • Manyverse: make sure DHT peers work as expected
  • ssb-conn: create this
  • ssb-conn scheduler: call ssb.bluetooth.nearby to stage peers, and periodically unstage them
  • ssb-conn scheduler: never connect to a blocked peer
  • ssb-conn scheduler: auto-connect to staged peers that we follow
  • ssb-local2: create this (Notice issue #271 (closed)), to expose a pull-stream source of LAN peers, broadcast also hash of the caps
  • ssb-conn scheduler: drain ssb-local2 stream and put {type, key} in staging, periodically unstage
  • ssb-conn scheduler: autopurge old staged peers using stagingUpdated fields from staging
  • Move ssb-conn out of ssb-legacy-conn, make ssb-conn a dependency in ssb-legacy-conn
  • ssb-legacy-conn: simplify as much as possible

SSB Gossip Rewrite

Goals

  • Simplify (untangle the implementation)
  • Modularize (split it into 4+ modules)
  • Generalize (use multiserver addresses and never assume host:port shape)
  • Customization via distros (i.e. instead of using lots of config fields in ssb-config, make it easy for developers to recombine and package their own gossip plugin)
  • Small (each of the modules should be about 200 lines of code each, in contrast current ssb-gossip is 700+ LOC)

Non-goals

(Things that could be done, and would be nice, but are not yet an actual problem, and by doing these we may introduce a new class unknown of problems)

  • Replace atomic-file with another database
  • Generalizing this work to non-SSB contexts

Proposed design

SSB CONN (Connections over Numerous Networks) as a stack of modules:

graph BT
    ssb-conn-query --> ssb-legacy-conn
    ssb-conn-db --> ssb-legacy-conn
    ssb-conn-db --> ssb-conn-query
    ssb-conn-hub --> ssb-conn-query
    ssb-conn-staging --> ssb-conn-query
    ssb-conn-hub --> ssb-conn-staging
    ssb-conn-staging --> ssb-legacy-conn
    ssb-conn-hub --> ssb-legacy-conn
    
    subgraph ssb-gossip alternatives
       ssb-legacy-conn
       ssb-conn
    end

ssb-conn-db

Takes care of persisting data concerning connections.

Contract

Needs
  • Path where the local database should be stored
  • atomic-file
Provides
  • CRUD API, key-value style where string address is the key
  • multiserver-address validation of the given addresses upon insert
  • Simple query APIs such as "best peer(s) under certain criteria"
  • Possibility to store peers that don't have a public crypto identity (e.g. rooms)
  • API to listen to changes to the DB
    • Important if a ssb-gossip plugin wants to always attempt connect upon DB insertion

ssb-conn-hub

Takes care of managing connections active during runtime.

Contract

Needs
  • pull-ping
Provides
  • API to connect to a specific peer (returns promise)
  • API to disconnect from a specific peer (returns promise)
  • API to list all currently connected peers (returns promise of array)
  • API to listen to changes to the set of currently connected peers
    • like gossip.changes
  • API to enable/disable a mode (a type of peer)
  • API to listen to modes being switched on/off
  • API to live detect the status
    • Offline: OFF (all connectivity modes are turned off)
    • Standby: ON (some connectivity modes are on), DISCONNECTED, and IDLE
    • Seeking: ON, DISCONNECTED, and BUSY
    • Online: ON, CONNECTED to some

ssb-conn-utils

Provides common connection-establishing tactics and algorithms. Perhaps this should be a Wiki with many independent utils modules?

Contract

Needs
  • ssb-conn-db
  • ssb-conn-hub
Provides
  • API to reconnect all
  • API to select peers based on criteria
  • API to connect to one peer that matches a criteria
    • For example, gossip/schedule.js::connect() function
    • Perhaps this is just utils::select() followed by a hub::connect()?
  • API to connect with exponential capped backoff upon errors
  • Listen to Hub events (such as connect/disconnect) and store health stats and ping to the db
  • API to disconnect one peer that matches a criteria

ssb-example-gossip

Template project showing how to setup your own ssb-gossip. Draft:

function ssbCONN(connDB, connHub) {
    connHub.onStatusChange((status) => {
        onSomeEvent(() => {
            utils.connectToMany(someCriteria)
        })
    })
    
    connHub.onConnect(() => {
        if (connHub.amount > MAX) {
           utils.disconnectOne(someCriteria) 
        }
    })
    
    return pluginAPI
}

ssb-legacy-gossip

Mimics as much as possible the current ssb-gossip plugin.

Mapping the current gossip plugin

Features

Exports

  • add: sync
    • insert a peer's address to the local gossip db
  • remove: sync
    • remove a peer's address from the local gossip db
  • enable: sync
    • Enable a type (local/global/seed) of connections to happen
  • disable: sync
    • Disable a type (local/global/seed) of connections from happening
  • connect: async
    • connect to a given address, AND add it to the local gossip db
  • peers: sync
    • Fetch all currently connected peers
  • changes: source
    • Listen to changes to set of currently connected peers
  • reconnect: sync
    • Nuke current connections and try to connect to any peers
  • ping: duplex
    • Used internally to measure latency and clock skew

Imports

  • has-network
    • To detect whether the current network interfaces give us any connection beyond the local device
  • on-change-network
  • on-wakeup
  • ip
    • To detect whether an IP address is private, or whether it is loopback
  • statistics
    • To calculate stats on duration to connect
  • atomic-file
    • As the database

Behavioral

  • Reconnect on network interface changes
    • Observe network interfaces, and when they change, call reconnect
  • Reconnect on computer waking up from sleep mode
    • Send heartbeats to self, and if some are missing, it means computer slept
  • Analyze good peers to connect to
    • Based on ping stats
    • Based on whether they have been responding to our attempts
  • On startup, read the SSB log for messages type "pub", and attempt connection
  • Schedule new connections upon startup
  • Schedule new connections periodically
  • Do not allow connections to exceed a maximum number, from config
  • Periodically save in-memory peers to the local gossip database
  • Scheduling connections always has a random delay
    • To prevent deadlock dancing with a remote peer following the same periodic rythym
  • Some peer types (e.g. local) are not loaded from db
  • On incoming (expected) connection, setup ping, update db, setup teardown logic
  • All peers must have a key (public identity)

Course of action

Basically, create ssb-legacy-gossip and literally just import ssb-gossip as is, then gradually write the other pieces, replacing parts as we go. In other words gradual migration, not sudden rewrite. See the top of this issue for a task list.

The idea is to use ssb-legacy-gossip in Manyverse, and gradually rewrite the internals, always using ssb-legacy-gossip in production. Once ssb-legacy-gossip is fully rewritten and conforms to the previous behavior, we can start building ssb-standard-gossip with new behavior.

ssb-standard-gossip can introduce:

  • Impossibility to connect with a blocked peer
  • Connect based on hops distance (see %PyiK5ksJlFD+cZCj5OZo7o7OdZxf6LaopOiwK75Ib1s=.sha256)
  • Exponential capped backoff on a per node basis
    • As a utility in ssb-conn-utils?
    • As an option given to ssb-conn-hub::connect(opts)?
  • Configurability of ssb-standard-gossip through ssb-config (while allowing other ssb-gossip implementations to ignore or not those configs)
  • Etc

For reference, these are the instances where high-level who-to-connect logic is coded:

References:

Edited by staltz