Skip to content
This repository was archived by the owner on Mar 14, 2023. It is now read-only.

feat: add implementation #2

Merged
merged 10 commits into from
Dec 10, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 2 additions & 9 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ name: ci
on:
push:
branches:
- master
- main
pull_request:
branches:
- master
- main

jobs:
check:
Expand All @@ -14,13 +14,6 @@ jobs:
- uses: actions/checkout@v2
- run: yarn
- run: yarn lint
- run: yarn build
- uses: gozala/[email protected]
- run: yarn aegir dep-check -- -i aegir
- uses: ipfs/aegir/actions/bundle-size@master
name: size
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
test-node:
needs: check
runs-on: ${{ matrix.os }}
Expand Down
28 changes: 28 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
FROM node:lts-alpine

# Install deps
RUN apk add --update git build-base python3

# Get dumb-init to allow quit running interactively
RUN wget -O /usr/local/bin/dumb-init https://github.com/Yelp/dumb-init/releases/download/v1.2.0/dumb-init_1.2.0_amd64 && chmod +x /usr/local/bin/dumb-init

# Setup directories for the `node` user
RUN mkdir -p /home/node/app/hop-relay-server/node_modules && chown -R node:node /home/node/app/hop-relay-server

WORKDIR /home/node/app/hop-relay-server

# Install node modules
COPY package.json ./
# Switch to the node user for installation
USER node
RUN npm install --production

# Copy over source files under the node user
COPY --chown=node:node ./src ./src
COPY --chown=node:node ./README.md ./

ENV DEBUG libp2p*

# Available overrides (defaults shown):
# Server logging can be enabled via the DEBUG environment variable
CMD [ "/usr/local/bin/dumb-init", "node", "src/bin.js"]
165 changes: 163 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,163 @@
# js-libp2p-hop-relay-server
A out of the box libp2p relay server with HOP
# js-libp2p-relay-server <!-- omit in toc -->

[![](https://img.shields.io/badge/made%20by-Protocol%20Labs-blue.svg?style=flat-square)](http://protocol.ai)
[![](https://img.shields.io/badge/project-libp2p-yellow.svg?style=flat-square)](http://libp2p.io/)
[![](https://img.shields.io/badge/freenode-%23libp2p-yellow.svg?style=flat-square)](http://webchat.freenode.net/?channels=%23libp2p)
[![](https://img.shields.io/discourse/https/discuss.libp2p.io/posts.svg)](https://discuss.libp2p.io)
[![GitHub Workflow Status](https://img.shields.io/github/workflow/status/libp2p/js-libp2p-relay-server/ci?label=ci&style=flat-square)](https://github.com/libp2p/js-libp2p-relay-server/actions?query=branch%3Amaster+workflow%3Aci+)

> An out of the box libp2p relay server with HOP

## Lead Maintainer <!-- omit in toc -->

[Vasco Santos](https://github.com/vasco-santos)

## Table of Contents<!-- omit in toc -->

- [Background](#background)
- [Usage](#usage)
- [Install](#install)
- [CLI](#cli)
- [Docker](#docker)
- [SSL](#ssl)
- [Configuration](#configuration)
- [Metrics](#metrics)
- [Discovery](#discovery)
- [Docker](#docker)
- [Debug](#debug)
- [Contribute](#contribute)
- [License](#license)

## Background

Libp2p nodes acting as circuit relay aim to establish connectivity between libp2p nodes (e.g. IPFS nodes) that wouldn't otherwise be able to establish a direct connection to each other.

A relay is needed in situations where nodes are behind NAT, reverse proxies, firewalls and/or simply don't support the same transports (e.g. go-libp2p vs. browser-libp2p). The circuit relay protocol exists to overcome those scenarios. Nodes with the `auto-relay` feature enabled can automatically bind themselves on a relay to listen for connections on their behalf.

You can read more in its [SPEC](https://github.com/libp2p/specs/tree/master/relay).

## Usage

### Install

```bash
> npm install --global libp2p-relay-server
```

Now you can use the cli command `libp2p-relay-server` to spawn an out of the box libp2p relay server.

### CLI

After installing the relay server, you can use its binary. It accepts several arguments: `--peerId`, `--listenMultiaddrs`, `--announceMultiaddrs`, `--metricsPort`, `--disableMetrics` and `--disablePubsubDiscovery`.

```sh
libp2p-relay-server [--peerId <jsonFilePath>] [--listenMultiaddrs <ma> ... <ma>] [--announceMultiaddrs <ma> ... <ma>] [--metricsPort <port>] [--disableMetrics] [--disablePubsubDiscovery]
```

The main configuration you *should* include are the `PeerId` and `Multiaddrs`, which are detailed next. Using a consistent PeerId will ensure your node's identity is consistent across restarts, and the Multiaddrs will allow you to appropriate bind your local and external addresses so that other peers can connect to you.

#### PeerId

You can create a [PeerId](https://github.com/libp2p/js-peer-id) via its [CLI](https://github.com/libp2p/js-peer-id#cli).

Once you have a generated PeerId json file, you can start the relay with that PeerId by specifying its path via the `--peerId` flag:

```sh
peer-id --type=ed25519 > id.json
libp2p-relay-server --peerId ./id.json
```

#### Multiaddrs

You can specify the libp2p rendezvous server listen and announce multiaddrs. This server is configured with [libp2p-tcp](https://github.com/libp2p/js-libp2p-tcp) and [libp2p-websockets](https://github.com/libp2p/js-libp2p-websockets), so you will only be able to specify addresses for these transports.

```sh
libp2p-relay-server --peerId /path/to/peer-id.json --listenMultiaddrs '/ip4/127.0.0.1/tcp/15002/ws' '/ip4/127.0.0.1/tcp/8001' --announceMultiaddrs '/dns4/test.io/tcp' '/dns4/test.io/tcp/443/wss'
```

By default it listens on `/ip4/127.0.0.1/tcp/8000` and `/ip4/127.0.0.1/tcp/15003/ws`. It has no announce multiaddrs specified.

### Docker

When running the relay server in Docker, you can configure the same parameters via environment variables, as follows:

```sh
PEER_ID='/etc/opt/relay/id.json'
LISTEN_MULTIADDRS='/ip4/127.0.0.1/tcp/15002/ws,/ip4/127.0.0.1/tcp/8001'
ANNOUNCE_MULTIADDRS='/dns4/test.io/tcp/443/wss,/dns6/test.io/tcp/443/wss'
```

Please note that you should expose the listening ports with the docker run command. The default ports used are `8003` for the metrics, `8000` for the tcp listener and `150003` for the websockets listener.

Example:

```sh
peer-id --type=ed25519 > id.json
docker build . -t libp2p-relay
docker run -p 8003:8003 -p 15002:15002 -p 8000:8000 \
-e LISTEN_MULTIADDRS='/ip4/127.0.0.1/tcp/8000,/ip4/127.0.0.1/tcp/15002/ws' \
-e ANNOUNCE_MULTIADDRS='/dns4/localhost/tcp/8000,/dns4/localhost/tcp/15002/ws' \
-e PEER_ID='/etc/opt/relay/id.json' \
-v $PWD/id.json:/etc/opt/relay/id.json \
-d libp2p-relay
```

### SSL

You should setup an SSL certificate with nginx and proxy to the API. You can use a service that already offers an SSL certificate with the server and configure nginx, or you can create valid certificates with for example [Letsencrypt](https://certbot.eff.org/lets-encrypt/osx-nginx). Letsencrypt won’t give you a cert for an IP address (yet) so you need to connect via SSL to a domain name.

With this, you should specify in your relay the announce multiaddrs for your listening transports. This is specially important for browser peers that will leverage this relay, as browser nodes can only dial peers behind a `DNS+WSS` multiaddr.

## Configuration

Besides the `PeerId` and `Multiaddrs`, this server can also have other configurations for fine tuning.

### Metrics

Metrics are enabled by default on `/ip4/127.0.0.1/tcp/8003` via Prometheus. This port can also be modified with:

```sh
libp2p-relay-server --metricsPort '8008'
```

Moreover, metrics can also be disabled with:

```sh
libp2p-relay-server --disableMetrics
```
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd just leave configuring this out, keep it simple. It binds to the global space out of the box, so just avoid people doing the wrong thing. IMO if people need to configure this in depth they can use the config as a template. The point of this repo is basically to get people up and running quickly, so we should just reduce the noise.

What's the bare minimum I need to do to get this running? The readme should focus on that. I think a consistent PeerId and Announce addresses are the only thing I should actually need to set. I'd prioritize and separate the need to have, from the optional stuff.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I divided them in two now. The required/recommended is in the usage section: peerId, multiaddrs and ssl

Then a new configuration section created for fine tuning of the image


### Discovery

A discovery module [libp2p/js-libp2p-pubsub-peer-discovery](https://github.com/libp2p/js-libp2p-pubsub-peer-discovery) is configured and enabled by default. It can be disabled with:

```sh
libp2p-relay-server --disablePubsubDiscovery
```

### Docker
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are 2 docker sections. Is this necessary? There is definitely duplicate content here.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, it includes the other configurable options that are not required/recommended as the CLI. I can look into simplifying it a bit, but I think it does not make sense to merge them as they should really be in the same section as the other optional parameters


On docker you can also specify the configurations above with the following environment variables:

```sh
METRICS_PORT='8008'
DISABLE_METRICS='true'
DISABLE_PUBSUB_DISCOVERY='true'
```

Please note that you should expose expose the used ports with the docker run command.

### Debug

You can debug the relay by setting the `DEBUG` environment variable. For instance, you can set it to `libp2p*`. These logs can be noisy so you may wish to tune the namespaces that are logging, see the [Debug module](https://github.com/visionmedia/debug) for detailed usage.

## Contribute

Feel free to join in. All welcome. Open an [issue](https://github.com/libp2p/js-libp2p-relay-server/issues)!

This repository falls under the IPFS [Code of Conduct](https://github.com/ipfs/community/blob/master/code-of-conduct.md).

[![](https://cdn.rawgit.com/jbenet/contribute-ipfs-gif/master/img/contribute.gif)](https://github.com/ipfs/community/blob/master/contributing.md)

## License

MIT - Protocol Labs 2020
45 changes: 40 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,22 +1,57 @@
{
"name": "libp2p-hop-relay-server",
"name": "libp2p-relay-server",
"version": "0.0.0",
"description": "A out of the box libp2p relay server with HOP",
"leadMaintainer": "Vasco Santos <[email protected]>",
"main": "src/index.js",
"bin": {
"libp2p-relay-server": "src/bin.js"
},
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
"lint": "aegir lint",
"test": "aegir test -t node",
"test:node": "aegir test -t node",
"build": "aegir build",
"release": "aegir release",
"release-minor": "aegir release --type minor",
"release-major": "aegir release --type major",
"docs": "aegir docs",
"size": "aegir build -b"
},
"files": [
"src",
"dist"
],
"repository": {
"type": "git",
"url": "git+https://github.com/vasco-santos/js-libp2p-hop-relay-server.git"
},
"keywords": [
"libp2p"
"libp2p",
"relay",
"auto relay",
"hop"
],
"author": "",
"author": "Vasco Santos <[email protected]>",
"license": "MIT",
"bugs": {
"url": "https://github.com/vasco-santos/js-libp2p-hop-relay-server/issues"
},
"homepage": "https://github.com/vasco-santos/js-libp2p-hop-relay-server#readme"
"homepage": "https://github.com/vasco-santos/js-libp2p-hop-relay-server#readme",
"dependencies": {
"libp2p": "libp2p/js-libp2p#0.30.x",
"libp2p-gossipsub": "^0.7.0",
"libp2p-mplex": "^0.10.1",
"libp2p-noise": "^2.0.1",
"libp2p-pubsub-peer-discovery": "^3.0.0",
"libp2p-tcp": "^0.15.1",
"libp2p-websockets": "^0.14.0",
"menoetius": "0.0.2",
"minimist": "^1.2.5",
"multiaddr": "^8.1.1",
"peer-id": "^0.14.2"
},
"devDependencies": {
"aegir": "^29.1.0"
}
}
90 changes: 90 additions & 0 deletions src/bin.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
#!/usr/bin/env node

'use strict'

// Usage: $0 [--peerId <jsonFilePath>] [--listenMultiaddrs <ma> ... <ma>] [--announceMultiaddrs <ma> ... <ma>]
// [--metricsPort <port>] [--disableMetrics] [--disablePubsubDiscovery]

/* eslint-disable no-console */

const debug = require('debug')
const log = debug('libp2p:relay:bin')

const fs = require('fs')
const http = require('http')
const menoetius = require('menoetius')
const argv = require('minimist')(process.argv.slice(2))

const PeerId = require('peer-id')

const { getAnnounceAddresses, getListenAddresses } = require('./utils')
const createRelay = require('./index')

async function main () {
// Metrics
let metricsServer
const metrics = !(argv.disableMetrics || process.env.DISABLE_METRICS)
const metricsPort = argv.metricsPort || argv.mp || process.env.METRICS_PORT || '8003'

// multiaddrs
const listenAddresses = getListenAddresses(argv)
const announceAddresses = getAnnounceAddresses(argv)

log(`listenAddresses: ${listenAddresses.map((a) => a)}`)
announceAddresses.length && log(`announceAddresses: ${announceAddresses.map((a) => a)}`)

// Discovery
const pubsubDiscoveryEnabled = !(argv.disablePubsubDiscovery || process.env.DISABLE_PUBSUB_DISCOVERY)

// PeerId
let peerId
if (argv.peerId || process.env.PEER_ID) {
const peerData = fs.readFileSync(argv.peerId || process.env.PEER_ID)
peerId = await PeerId.createFromJSON(JSON.parse(peerData))
log('PeerId provided was loaded.')
} else {
peerId = await PeerId.create()
log('You are using an automatically generated peer.')
log('If you want to keep the same address for the server you should provide a peerId with --peerId <jsonFilePath>')
}

// Create Relay
const relay = await createRelay({
peerId,
listenAddresses,
announceAddresses,
pubsubDiscoveryEnabled
})

await relay.start()
console.log('Relay server listening on:')
relay.multiaddrs.forEach((m) => console.log(m))

if (metrics) {
log('enabling metrics')
metricsServer = http.createServer((req, res) => {
if (req.url !== '/metrics') {
res.statusCode = 200
res.end()
}
})

menoetius.instrument(metricsServer)

metricsServer.listen(metricsPort, '0.0.0.0', () => {
console.log(`metrics server listening on ${metricsPort}`)
})
}

const stop = async () => {
console.log('Stopping...')
await relay.stop()
metricsServer && await metricsServer.close()
process.exit(0)
}

process.on('SIGTERM', stop)
process.on('SIGINT', stop)
}

main()
Loading