# Infra how-to
[ToC]
## Node types
We can split nodes in two category:
- Messaging nodes: `peer` and `replication`
- Routing nodes: `relay`, `bootstrap` and `rdvp`
Messaging nodes can send messages:
- `peer` is a full Berty user that can register to a group by itself, send contact request, read and write messages on a group.
- `replication` is a server with an API allowing a `peer` to add it to a group. It will only provide high availability by forwarding messages on a group to peers without being able to read them.
Routing nodes allow Messaging nodes to connect to each other:
- Network:
- `relay` will provide NAT Traversal to a peer that can't accept inbound connection using a TURN-like protocol (see [libp2p doc](https://docs.libp2p.io/concepts/circuit-relay/))
- Discovery
- `bootstrap` will exchange peers addresses with any peer that connect to it, useful for DHT
- `rdvp` a kind of key/value store where any peer can register its address under an arbitrary name (e.g `Set({foo: <ip-address>})` and `Get(foo) // return <ip-address`)
## Instanciation flow
There is an order to instantiate the different node types, because `peer` and `replication` types (Messaging nodes) needs to know the addresses of the `relay`, `bootstrap` and `rdvp` types (Routing nodes).
So the order in which the different types of nodes should be instantiated is:
1. `relay` / `rdvp` / `bootstrap` (Routing nodes)
3. `replication` / `peer` (Messaging nodes)
Knowing also that after reflection on our side, the simplest for you would be to create the Berty groups (in the sense protocol / conversation group) and to add the replication servers to them via a temporary "setup daemon". This will require to implement a go program on your side talking with the daemon gRPC API (see [Daemon gRPC API](#Daemon-gRPC-API)).
So the complete flow should be:
1. instanciate `relay`, `rdvp` and `boostrap` -> save their addresses
2. instanciate `replication` (specify `relay`, `bootstrap` and `rdvp` addresses if needed)
3. create groups and add `replication` to them using a setup daemon -> save group invitations / kill setup daemon
4. instanciate `peer` (with group invitations and specify `relay`, `bootstrap` and `rdvp` addresses if needed)
5. run tests by sending gRPC calls to `peer`
## Daemon gRPC API
When you run a daemon using `go run ./go/cmd/berty daemon`, it will by default listen on `localhost:9091` and provide protocol and messenger APIs.
You can specify a custom listener using `-node.listeners` flag.
You can check the API protos in `api/protocoltypes` and `api/messengertypes`.
For example, to create a group and print an invit using a running daemon listening on default address, you will have to mix both API:
```go=
package main
import (
"context"
"fmt"
"berty.tech/berty/v2/go/pkg/messengertypes"
"berty.tech/berty/v2/go/pkg/protocoltypes"
"google.golang.org/grpc"
)
func main() {
ctx := context.Background()
host := "127.0.0.1:9091" // default daemon gRPC listener
// Init gRPC services (protocol and messenger)
cc, err := grpc.DialContext(ctx, host, grpc.FailOnNonTempDialError(true), grpc.WithInsecure())
if err != nil {
panic(err)
}
protocol := protocoltypes.NewProtocolServiceClient(cc)
messenger := messengertypes.NewMessengerServiceClient(cc)
// Create group using protocol
resCreate, err := protocol.MultiMemberGroupCreate(ctx, &protocoltypes.MultiMemberGroupCreate_Request{})
if err != nil {
panic(err)
}
// Print group invit on stdout using messenger
resInvit, err := messenger.ShareableBertyGroup(ctx, &messengertypes.ShareableBertyGroup_Request{
GroupPK: resCreate.GroupPK,
GroupName: "foo",
})
if err != nil {
panic(err)
}
fmt.Println(resInvit.GetWebURL())
}
```
You can find a more detailled example [here](https://github.com/gfanton/berty/blob/7101a36f002212f25fcf66e3d0d0f6f00255f47c/tool/berty-client/client.go).
## Instanciation detail
### Relay
The easiest way to instantiate a relay is to use the rdvp command (berty/go/cmd/rdvp). **Note**: The node will not act as an rdvp as long as it is only specified in the relay config of a peer.
The address you'll get will need to be specified in node config that need a relay.
With libp2p/IPFS we use multiaddress to specify the address to reach a peer, more info here: https://docs.libp2p.io/concepts/addressing/
#### Instanciate a relay
It's up to you to do it using a pre-gen key or not, just choose the more convenient way.
Knowing that with a pre-gen key:
- you can persist the relay peerID across restart (so it's multiaddress will still be valid).
- you can get the full multiaddress by crafting it (since you have a persistent peerID) and without having to read the log
When you will use the `serve` subcommand (see examples below), by default, the `-announce` flag will be unspecified and the `-l` flag will be specified with an empty string:
`-l=''`
But depending on the configuration of the connections, you will have to adapt these flags to set one or more listeners.
##### Without a pre-gen key
###### TCP / UDP
```bash
# You first need to know the public IP of the relay
$ export PUBLIC_IP4=52.158.21.203
# Specify a protocol between UDP and TCP (for this example)
$ export PROTOC=tcp
# You can choose a port or set it at 0 to use a random one
$ export PORT=4040
# Start the rdvp in relay mode (announce)
$ rdvp -log.format=json -log.file=./log.json serve \
-announce "/ip4/$PUBLIC_IP4/$PROTOC/$PORT" \
-l "/ip4/$PUBLIC_IP4/$PROTOC/$PORT"
# Get full multiaddr from logs
$ cat ./log.json | jq .maddr
"/ip4/52.158.21.203/tcp/4040/p2p/12D3KooWCS8MMcM4GuyFa7vc59R9TChh522dEy6raJQR5ZNKEnzm"
# NOTE: the last segment after p2p is a peerID
```
###### QUIC (over UDP only)
```bash
# You first need to know the public IP of the relay
$ export PUBLIC_IP4=52.158.21.203
# You can choose a port or set it at 0 to use a random one
$ export PORT=4040
# Start the rdvp in relay mode (announce)
$ rdvp -log.format=json -log.file=./log.json serve
-announce "/ip4/$PUBLIC_IP4/udp/$PORT/quic" \
-l "/ip4/$PUBLIC_IP4/udp/$PORT/quic"
# Get full multiaddr from logs
$ cat ./log.json | jq.maddr
"/ip4/52.158.21.203/udp/4040/quic/p2p/12D3KooWCS8MMcM4GuyFa7vc59R9TChh522dEy6raJQR5ZNKEnzm"
# NOTE: the last segment after p2p is a peerID
```
###### Websocket (over TCP only)
```bash
# You first need to know the public IP of the relay
$ export PUBLIC_IP4=52.158.21.203
# You can choose a port or set it at 0 to use a random one
$ export PORT=4040
# Start the rdvp in relay mode (announce)
$ rdvp -log.format=json -log.file=./log.json serve
-announce "/ip4/$PUBLIC_IP4/tcp/$PORT/ws" \
-l "/ip4/$PUBLIC_IP4/tcp/$PORT/ws"
# Get full multiaddr from logs
$ cat ./log.json | jq .maddr
"/ip4/52.158.21.203/tcp/4040/ws/p2p/12D3KooWCS8MMcM4GuyFa7vc59R9TChh522dEy6raJQR5ZNKEnzm"
# NOTE: the last segment after p2p is a peerID
```
##### With a pre-gen key
**Note**: Refer to QUIC and Websocket section above to see the format of a multiaddr with these transports
```bash
# You first need to know the public IP of the relay
$ export PUBLIC_IP4=52.158.21.203
# Specify a protocol between UDP and TCP (for this example)
$ export PROTOC=tcp
# You can choose a port or set it at 0 to use a random one
$ export PORT=4040
# Generate a secret key
$ rdvp genkey > ./key.sk
# Get peer id from this secret key
$ rdvp sharekey -pk `cat ./key.sk` > ./peerID
# Export peerID
$ export PEER_ID=$(cat ./peerID)
# Start the rdvp in relay mode (announce) using secret key
$ rdvp serve -pk `cat ./key.sk` \
-anounce "/ip4/$PUBLIC_IP4/$PROTOC/$PORT" \
-l "/ip4/$PUBLIC_IP4/$PROTOC/$PORT"
# The full multiaddr will be
$ echo "/ip4/$PUBLIC_IP4/$PROTOC/$PORT/p2p/$PEER_ID"
"/ip4/52.158.21.203/tcp/4040/p2p/12D3KooWCS8MMcM4GuyFa7vc59R9TChh522dEy6raJQR5ZNKEnzm"
```
### RDVP
Will be almost the same as relay section above with few differences:
- You can (optionnaly) persist the rdvp DB across restart by using the `-db file.db` flag
- You won't use `-announce <multiaddr>` as it's only useful in relay mode
When you will use the `serve` subcommand (see examples below), by default, the `-l` flag will be specified with an empty string:
`-l=''`
But depending on the configuration of the connections, you will have to adapt this flag to set one or more listeners.
#### Instanciate a RDVP
##### Without a pre-gen key
###### TCP / UDP
```bash
# You first need to know the public IP of the relay
$ export PUBLIC_IP4=52.158.21.203
# Specify a protocol between UDP and TCP (for this example)
$ export PROTOC=tcp
# You can choose a port or set it at 0 to use a random one
$ export PORT=4040
# Start the rdvp with persistent DB
$ rdvp -log.format=json -log.file=./log.json \
serve -l "/ip4/$PUBLIC_IP4/$PROTOC/$PORT"
# Get full multiaddr from logs
$ cat ./log.json | jq .maddr
"/ip4/52.158.21.203/tcp/4040/p2p/12D3KooWCS8MMcM4GuyFa7vc59R9TChh522dEy6raJQR5ZNKEnzm"
# NOTE: the last segment after p2p is a peerID
```
###### QUIC (over UDP only)
```bash
# You first need to know the public IP of the relay
$ export PUBLIC_IP4=52.158.21.203
# You can choose a port or set it at 0 to use a random one
$ export PORT=4040
# Start the rdvp in relay mode (announce)
$ rdvp -log.format=json -log.file=./log.json \
serve -l "/ip4/$PUBLIC_IP4/udp/$PORT/quic"
# Get full multiaddr from logs
$ cat ./log.json | jq.maddr
"/ip4/52.158.21.203/udp/4040/quic/p2p/12D3KooWCS8MMcM4GuyFa7vc59R9TChh522dEy6raJQR5ZNKEnzm"
# NOTE: the last segment after p2p is a peerID
```
###### Websocket (over TCP only)
```bash
# You first need to know the public IP of the relay
$ export PUBLIC_IP4=52.158.21.203
# You can choose a port or set it at 0 to use a random one
$ export PORT=4040
# Start the rdvp in relay mode (announce)
$ rdvp -log.format=json -log.file=./log.json \
serve -l "/ip4/$PUBLIC_IP4/tcp/$PORT/ws"
# Get full multiaddr from logs
$ cat ./log.json | jq .maddr
"/ip4/52.158.21.203/tcp/4040/ws/p2p/12D3KooWCS8MMcM4GuyFa7vc59R9TChh522dEy6raJQR5ZNKEnzm"
# NOTE: the last segment after p2p is a peerID
```
##### With a pre-gen key
**Note**: Refer to QUIC and Websocket section above to see the format of a multiaddr with these transports
```bash
# You first need to know the public IP of the relay
$ export PUBLIC_IP4=52.158.21.203
# Specify a protocol between UDP and TCP (for this example)
$ export PROTOC=tcp
# You can choose a port or set it at 0 to use a random one
$ export PORT=4040
# Generate a secret key
$ rdvp genkey > ./key.sk
# Get peer id from this secret key
$ rdvp sharekey -pk `cat ./key.sk` > ./peerID
# Export peerID
$ export PEER_ID=$(cat ./peerID)
# Start the rdvp in relay mode (announce) using secret key
$ rdvp serve -pk `cat ./key.sk` -l "/ip4/$PUBLIC_IP4/$PROTOC/$PORT"
# The full multiaddr will be
$ echo "/ip4/$PUBLIC_IP4/$PROTOC/$PORT/p2p/$PEER_ID"
"/ip4/52.158.21.203/tcp/4040/p2p/12D3KooWCS8MMcM4GuyFa7vc59R9TChh522dEy6raJQR5ZNKEnzm"
```
### Bootstrap
You can launch a bootstrap node by launching a Berty daemon with the right flags:
- `-p2p.mdns=false`
- `-p2p.bootstrap=':none:'`
- `-p2p.rdvp=':none'`
- `-p2p.static-relays=':none'`
- `-p2p.tinder-dht-driver=false`
- `-p2p.tinder-rdvp-driver=false`
By default, the swarm listener flag will be empty:
- `-p2p.swarm-listeners=':none:'`
But depending on the configuration of the connections, you will have to adapt this flag to set one or more listeners.
Example:
```yaml
Bootstrap:
type: bootstrap
amount: 1
connections:
- to: internet
transport: tcp
bandwidth: 100Mbps
reliability: 0,0
```
Will be run with:
```bash
berty daemon \
-p2p.mdns=false \
-p2p.bootstrap=':none:' \
-p2p.rdvp=':none' \
-p2p.static-relays=':none' \
-p2p.tinder-dht-driver=false \
-p2p.tinder-rdvp-driver=false \
-p2p.swarm-listeners="/ip4/$PUBLIC_IP/tcp/$PORT"
```
### Group creation
**NOTE**: You could merge [Group creation](#Group-creation) and [Replication](#Replication) in one step if it seems better to you.
To create a group:
1. Run a berty daemon
```bash
berty daemon
```
2. Run a [custom go program](https://hackmd.io/6HsBjNkPTM6xvDo3WJq1bw?both#Daemon-gRPC-API) that:
2A. Create a group
```
gRPC call method:
protocoltypes.ProtocolService.MultiMemberGroupCreate
```
2B. Create an invitation to this group
```
gRPC call method:
protocoltypes.ProtocolService.MultiMemberGroupInvitationCreate
with:
protocoltypes.ProtocolService.MultiMemberGroupInvitationCreate.Request{group_pk: <value of protocoltypes.ProtocolService.MultiMemberGroupCreate.Reply.group_pk>}
```
2C. Save the output of `protocoltypes.ProtocolService.MultiMemberGroupInvitationCreate.Reply.group` as an invit
3. Kill the setup daemon and the go custom program
### Replication
A peer need a token to authenticate when using the API of a replication server.
So the flow should be:
1. Run a replication server
```bash
# The public IP of the replication server
$ export REPL_IP=1.1.1.1
# The port of the replication server API
$ export REPL_PORT=1111
# Run the replication server
berty repl-server \
-node.listeners "/ip4/$REPL_IP/tcp/$PORT/grpc" \
-node.auth-secret 0123456789abcdefghijklmnopqrstuvwxyzABCDEFG \
-node.auth-pk sUpMtDYs4fSOMQ1EMZgkKXgz9l3U822ByT57liN4TG0
```
2. Run a token server connected to the replication server
```bash
# The public IP of the token server
$ export TOKEN_IP=2.2.2.2
# The port of the token server API
$ export TOKEN_PORT=2222
# Run the token server
berty token-server \
-no-click \ # required for automation
-svc "rpl@$REPL_IP:$REPL_PORT" \
-http.listener "$TOKEN_IP:$TOKEN_PORT" \
-auth.secret 0123456789abcdefghijklmnopqrstuvwxyzABCDEFG \
-auth.sk ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefg
```
3. Run a berty daemon
```bash
berty daemon
```
4. Run a [custom go program](https://hackmd.io/6HsBjNkPTM6xvDo3WJq1bw?both#Daemon-gRPC-API) that takes as parameter a group invit, the token server address and run this flow:
4A. Request a token to the token server
```
gRPC call method:
protocoltypes.ProtocolService.AuthServiceInitFlow
with:
protocoltypes.AuthServiceInitFlow.Request{auth_url: "https://$TOKEN_IP:$TOKEN_PORT"}
```
Then write a go function equivalent to:
```
export CALLBACK_URL=$(curl "<value of protocoltypes.AuthServiceInitFlow.Reply.url>" -s | grep href= | cut -d'"' -f2 | sed 's/&/\&/')
```
4B. Register to the replication server
```
gRPC call method:
protocoltypes.ProtocolService.AuthServiceCompleteFlow
with:
protocoltypes.AuthServiceCompleteFlow.Request{callback_url: "$CALLBACK_URL"}
```
4C. Join the group to which you need to add this replication server (it will be auto-added on join)
```
gRPC call method:
protocoltypes.ProtocolService.MultiMemberGroupJoin
with:
protocoltypes.MultiMemberGroupJoin.Request{group: <invitation>}
```
5. Kill the setup daemon, go custom program and token server
### Peer
To instantiate a peer, you will have to run `berty daemon` command. One concern is that a lot of parameters are set by default and you will have to explicitly disable them.
The list of default flags a peer needs are:
- `-p2p.mdns=false`
- `-p2p.static-relays=':none'`
- `-p2p.bootstrap=':none:'`
- `-p2p.dht-randomwalk=false`
- `-p2p.tinder-dht-driver=false`
- `-p2p.rdvp=':none'`
- `-p2p.tinder-rdvp-driver=false`
- `-p2p.swarm-listeners=':none:'`
But depending on it's config, you will have adapt these flags.
#### Flags
##### Connections
```yaml
Example_Peer:
type: peer
amount: 1
connections:
- to: lan_1
transport: quic
bandwidth: 100Mbps
reliability: 0,0
```
Will be run using this flag:
```bash
berty daemon \
-p2p.mdns=false \
-p2p.static-relays=':none' \
-p2p.bootstrap=':none:' \
-p2p.dht-randomwalk=false \
-p2p.tinder-dht-driver=false \
-p2p.rdvp=':none' \
-p2p.tinder-rdvp-driver=false \
-p2p.swarm-listeners='/ip4/$PRIVATE_IP_LAN_1/udp/$PORT/quic'
```
##### Routers relay
```yaml
Example_Peer:
type: peer
amount: 1
connections:
- to: internet
transport: quic
bandwidth: 100Mbps
reliability: 0,0
routers:
- type: relay
address: '/ip4/51.159.21.214/udp/4040/quic/p2p/QmdT7AmhhnbuwvCpa5PH1ySK9HJVB82jr3fo1bxMxBPW6p'
```
Will be run using this flag:
```bash
berty daemon \
-p2p.mdns=false \
-p2p.bootstrap=':none:' \
-p2p.dht-randomwalk=false \
-p2p.tinder-dht-driver=false \
-p2p.rdvp=':none' \
-p2p.tinder-rdvp-driver=false \
-p2p.swarm-listeners='/ip4/$PUBLIC_IP/udp/$PORT/quic' \
-p2p.static-relays='/ip4/51.159.21.214/udp/4040/quic/p2p/QmdT7AmhhnbuwvCpa5PH1ySK9HJVB82jr3fo1bxMxBPW6p'
```
##### Routers bootstrap
```yaml
Example_Peer:
type: peer
amount: 1
connections:
- to: lan_1
transport: quic
bandwidth: 100Mbps
reliability: 0,0
routers:
- type: bootstrap
address: '/ip4/104.131.131.82/tcp/4001/p2p/QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ'
```
Will be run using this flag:
```bash
berty daemon \
-p2p.mdns=false \
-p2p.static-relays=':none' \
-p2p.rdvp=':none' \
-p2p.tinder-rdvp-driver=false \
-p2p.swarm-listeners='/ip4/$PUBLIC_IP/udp/$PORT/quic' \
-p2p.bootstrap='/ip4/104.131.131.82/tcp/4001/p2p/QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ' \
-p2p.dht-randomwalk=true # Or unspecified because true is default \
-p2p.tinder-dht-driver=true # Or unspecified because true is default
```
##### Routers rdvp
```yaml
Example_Peer:
type: peer
amount: 1
connections:
- to: lan_1
transport: quic
bandwidth: 100Mbps
reliability: 0,0
routers:
- type: rdvp
address: '/ip4/104.131.131.82/tcp/4001/p2p/QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ'
```
Will be run using this flag:
```bash
berty daemon \
-p2p.mdns=false \
-p2p.static-relays=':none' \
-p2p.bootstrap=':none:' \
-p2p.dht-randomwalk=false \
-p2p.tinder-dht-driver=false \
-p2p.swarm-listeners='/ip4/$PUBLIC_IP/udp/$PORT/quic' \
-p2p.rdvp='/ip4/51.159.21.214/udp/4040/quic/p2p/QmdT7AmhhnbuwvCpa5PH1ySK9HJVB82jr3fo1bxMxBPW6p' \
-p2p.tinder-rdvp-driver=true # Or unspecified because true is default
```
#### Join group and run tests
For each group to join you will have to use the group invitation and connect to each `peer` (daemon) [using gRPC](#Daemon-gRPC-API).
1. Join a group
```
gRPC call method:
protocoltypes.ProtocolService.MultiMemberGroupJoin
with:
protocoltypes.MultiMemberGroupJoin.Request{group: <invitation>}
```
**TODO**: but you can check the code of Berty mini here: https://github.com/berty/berty/blob/master/go/cmd/berty/mini/view_group.go
2. Run a loop that receive messages and send back ACKs using these calls
3. Send text messages
4. Send media messages