Bifrost
Modular networking for Go with pluggable transports, RPC, WebRTC, WebAssembly support.
Install / Use
/learn @aperturerobotics/BifrostREADME

Introduction
Bifrost is a peer-to-peer communications engine with pluggable transports:
- Cross-platform: supports web browsers, servers, desktop, mobile, ...
- Efficient: multiplex many simultaneous streams over a single Link.
- Encryption: identify, authenticate, and encrypt each Link between peers.
- Flexible: use multiple transports, protocols, simultaneously.
- Meshing: supports multi-hop routing to a desired target peer w/ circuits.
- PubSub: publish/subscribe channels with pluggable implementations.
- Robust: uses Quic for lossless links over lossy transports.
Bifrost uses ControllerBus controllers and directives to send any protocol over any transport with extensive and flexible configuration.
Overview
Bifrost is designed around the following core concepts:
- Peer: a routable process or device with a keypair.
- Transport: a protocol which can create Links with other peers.
- Link: a connection between two peers over a Transport.
- Stream: channel of data between two Peer with a protocol type.
- RPC: request/reply and bidirectional streaming remote calls.
- PubSub: at-least-once delivery of messages to named topics.
- Signaling: exchanging messages between peers via a relay server.
Integrates with networking, pubsub, and RPC libraries like libp2p, starpc, and pion webrtc.
The network simulator, testbed, and in-proc transport can be used to write end-to-end tests as Go unit tests. The mock transports use identical code to the real transports, and appear the same to the application code.
The http packages provide a http server and a mechanism for attaching http handlers to the controller bus and using them to serve requests. There is also an implementation of attaching and looking up http clients on the bus.
EntityGraph exposes the internal state representation of Bifrost to visualizers and instrumentation via a graph-based inter-connected entity model.
Configuring each component as an independent controller makes it easy to adapt application code to different operating environments and protocols.
Examples
Bifrost can be used as either a Go library or a command-line / daemon.
The examples directory contains yaml files to configure the daemon, as well as "toys" which are self-contained Go program examples.
To install the CLI and daemon:
# Clone the repo and install.
git clone https://github.com/aperturerobotics/bifrost
cd ./bifrost/cmd/bifrost
go install -v
# Alternatively:
# Note: this currently fails on go >= 1.16 due to replace directives.
# See: https://github.com/golang/go/issues/44840
GO111MODULE=on go install -v github.com/aperturerobotics/bifrost/cmd/bifrost@master
Access help by adding the "-h" tag or running "bifrost help."
As a basic example, launch the daemon:
bifrost daemon \
--write-config \
--hold-open-links \
--pubsub floodsub \
--api-listen :5110 \
--udp-listen :5112
Check out the http forwarding example for a complete demo.
Daemon CLI
The Bifrost daemon is configured with a YAML ConfigSet and/or via the API.
NAME:
bifrost daemon - run a bifrost daemon
OPTIONS:
--hold-open-links if set, hold open links without an inactivity timeout [$BIFROST_HOLD_OPEN_LINKS]
--websocket-listen value if set, will listen on address for websocket connections, ex :5111 [$BIFROST_WS_LISTEN]
--udp-listen value if set, will listen on address for udp connections, ex :5112 [$BIFROST_UDP_LISTEN]
--establish-peers value if set, request establish links to list of peer ids [$BIFROST_ESTABLISH_PEERS]
--udp-peers value list of peer-id@address known UDP peers [$BIFROST_UDP_PEERS]
--websocket-peers value list of peer-id@address known WebSocket peers [$BIFROST_WS_PEERS]
--pubsub value if set, will configure pubsub from options: [floodsub] [$BIFROST_PUBSUB]
--config value, -c value path to configuration yaml file (default: "bifrost_daemon.yaml") [$BIFROST_CONFIG]
--write-config write the daemon config file on startup [$BIFROST_WRITE_CONFIG]
--node-priv value path to node private key, will be generated if doesn't exist (default: "bifrost_daemon.pem") [$BIFROST_NODE_PRIV]
--api-listen value if set, will listen on address for API connections, ex :5110 (default: none) [$BIFROST_API_LISTEN]
--prof-listen value if set, debug profiler will be hosted on the port, ex :8080 [$BIFROST_PROF_LISTEN]
These CLI flags are provided for convenience to quickly configure a daemon, and
the resulting config can be written to a file with --write-config for further
adjustments to be made. Note, however, that additional controllers are available
which are not yet exposed via these flags.
Client CLI
Most Bifrost functionality is exposed on the client CLI and RPC API:
- Mount a peer by loading a private key into the daemon.
- Forward incoming streams with a protocol ID to a multiaddress
- Proxy incoming connections to a listener to a remote peer
- Open a stream with a remote peer and a given protocol ID
- Accept a stream for a local peer with a given protocol ID
The client CLI has the following help output:
bifrost client command [command options] [arguments...]
COMMANDS:
local-peers returns local peer info
identify Private key will be loaded with a peer controller
subscribe Subscribe to a pubsub channel with a private key or mounted peer and publish base64 w/ newline delim from stdin.
forward Protocol ID will be forwarded to the target multiaddress
accept Single incoming stream with Protocol ID will be accepted
dial Single outgoing stream with Protocol ID will be dialed
listen Listen on the multiaddress and forward the connection to a remote stream.
controller-bus, cbus ControllerBus system sub-commands.
With the above operations, all stream handling and interaction with the network is exposed to the API and command line. Some examples:
# Note: you can edit bifrost_daemon.yaml to change settings.
# Once the daemon configuration exists, you can now just run:
bifrost daemon
# While the command is executing, the private key will be attached.
bifrost client identify --peer-priv priv-key.pem
# While the command is executing, a forwarding controller will be running.
# Protocol ID will be forwarded to the target multiaddress
# Handles HandleMountedStream directives by contacting the target.
# HTTP can be easily proxied through an encrypted stream this way.
bifrost client forward \
--peer-id <agent-id> \
--protocol-id /x/myproto \
--target /ip4/127.0.0.1/tcp/8000
# While the command is executing, a proxying controller will be running.
# Protocol ID will be proxied from the listen multiaddress to the target peer.
# Calls OpenStream to build a stream from <source-peer-id> to <target-peer-id>.
# HTTP can be easily proxied through an encrypted stream this way.
bifrost client listen \
--peer-id <target-peer-id> \
--from-peer-id <source-peer-id> \
--protocol-id /x/myproto \
--listen /ip4/127.0.0.1/tcp/8001
# Wait for a stream to be opened to the mounted peer with the protocol ID /x/myproto
# Standard output is the incoming data stream, standard input is the outgoing data stream.
# Standard error is used for logging.
bifrost client accept \
--local-peer-id <p
