Pages

Thursday, 28 September 2023

nostream

 A Nostr Relay written in TypeScript.

GitHub release GitHub issues GitHub stars GitHub top language GitHub forks GitHub license Coverage Status Sonarcloud quality gate Build status

This is a nostr relay, written in Typescript.

This implementation is production-ready. See below for supported features.

The project master repository is available on GitHub.

Deploy on Railway

Features

NIPs with a relay-specific implementation are listed here.

  • NIP-01: Basic protocol flow description
  • NIP-02: Contact list and petnames
  • NIP-04: Encrypted Direct Message
  • NIP-09: Event deletion
  • NIP-11: Relay information document
  • NIP-11a: Relay Information Document Extensions
  • NIP-12: Generic tag queries
  • NIP-13: Proof of Work
  • NIP-15: End of Stored Events Notice
  • NIP-16: Event Treatment
  • NIP-20: Command Results
  • NIP-22: Event created_at Limits
  • NIP-26: Delegated Event Signing
  • NIP-28: Public Chat
  • NIP-33: Parameterized Replaceable Events
  • NIP-40: Expiration Timestamp

Requirements

Standalone setup

  • PostgreSQL 14.0
  • Redis
  • Node v18
  • Typescript

Docker setups

  • Docker v20.10
  • Docker Compose v2.10

Local Docker setup

  • Docker Desktop v4.2.0 or newer
  • mkcert

WARNING: Docker distributions from Snap, Brew or Debian repositories are NOT SUPPORTED and will result in errors. Install Docker from their official guide ONLY.

Full Guide

Accepting Payments

  • Before you begin

    • Complete one of the Quick Start guides in this document
    • Create a .env file
    • On .nostr/settings.yaml file make the following changes:
      • Set payments.enabled to true
      • Set payments.feeSchedules.admission.enabled to true
      • Set limits.event.pubkey.minBalance to the minimum balance in msats required to accept events (i.e. 1000000 to require a balance of 1000 sats)
    • Choose one of the following payment processors: zebedee, nodeless, opennode, lnbits, lnurl
  • ZEBEDEE

    ZEBEDEE_API_KEY={YOUR_ZEBEDEE_API_KEY_HERE}
    
    • Follow the required steps for all payments processors
    • On .nostr/settings.yaml file make the following changes:
      • payments.processor to zebedee
      • paymentsProcessors.zebedee.callbackBaseURL to match your Nostream URL (e.g. https://{YOUR_DOMAIN_HERE}/callbacks/zebedee)
    • Restart Nostream (./scripts/stop followed by ./scripts/start)
    • Read the in-depth guide for more information: Set Up a Paid Nostr Relay with ZEBEDEE API
  • Nodeless

    • Complete the step "Before you begin"
    • Sign up for a new account, create a new store and take note of the store ID
    • Go to Profile > API Tokens and generate a new key and take note of it
    • Create a store webhook with your Nodeless callback URL (e.g. https://{YOUR_DOMAIN_HERE}/callbacks/nodeless) and make sure to enable all of the events. Grab the generated store webhook secret
    • Set NODELESS_API_KEY and NODELESS_WEBHOOK_SECRET environment variables with generated API key and webhook secret, respectively
    NODELESS_API_KEY={YOUR_NODELESS_API_KEY}
    NODELESS_WEBHOOK_SECRET={YOUR_NODELESS_WEBHOOK_SECRET}
    
    • On your .nostr/settings.yaml file make the following changes:
      • Set payments.processor to nodeless
      • Set paymentsProcessors.nodeless.storeId to your store ID
    • Restart Nostream (./scripts/stop followed by ./scripts/start)
  • OpenNode

    • Complete the step "Before you begin"

    • Sign up for a new account and get verified

    • Go to Developers > Integrations and setup two-factor authentication

    • Create a new API Key with Invoices permission

    • Set OPENNODE_API_KEY environment variable on your .env file

      OPENNODE_API_KEY={YOUR_OPENNODE_API_KEY}
      
    • On your .nostr/settings.yaml file make the following changes:

      • Set payments.processor to opennode
    • Restart Nostream (./scripts/stop followed by ./scripts/start)

  • LNBITS

    • Complete the step "Before you begin"

    • Create a new wallet on you public LNbits instance

      • Demo server must not be used for production
      • Your instance must be accessible from the internet and have a valid SSL/TLS certificate
    • Get wallet Invoice/read key (in Api docs section of your wallet)

    • set LNBITS_API_KEY environment variable with the Invoice/read key Key above on your .env file

      LNBITS_API_KEY={YOUR_LNBITS_API_KEY_HERE}
      
      • On your .nostr/settings.yaml file make the following changes:

        • Set payments.processor to lnbits
        • set lnbits.baseURL to your LNbits instance URL (e.g. https://{YOUR_LNBITS_DOMAIN_HERE}/)
        • Set paymentsProcessors.lnbits.callbackBaseURL to match your Nostream URL (e.g. https://{YOUR_DOMAIN_HERE}/callbacks/lnbits)
      • Restart Nostream (./scripts/stop followed by ./scripts/start)

    1. Alby or any LNURL Provider with LNURL-verify support

      • Complete the step "Before you begin"
      • Create a new account if you don't have an LNURL
      • On your .nostr/settings.yaml file make the following changes:
        • Set payments.processor to lnurl
        • Set lnurl.invoiceURL to your LNURL (e.g. https://getalby.com/lnurlp/your-username)
      • Restart Nostream (./scripts/stop followed by ./scripts/start)
    2. Ensure payments are required for your public key

      • Visit https://{YOUR-DOMAIN}/
      • You should be presented with a form requesting an admission fee to be paid
      • Fill out the form and take the necessary steps to pay the invoice
      • Wait until the screen indicates that payment was received
      • Add your relay URL to your favorite Nostr client (wss://{YOUR-DOMAIN}) and wait for it to connect
      • Send a couple notes to test
      • Go to https://websocketking.com/ and connect to your relay (wss://{YOUR_DOMAIN})
      • Convert your npub to hexadecimal using a Key Converter
      • Send the following JSON message: ["REQ", "payment-test", {"authors":["your-pubkey-in-hexadecimal"]}]
      • You should get back the few notes you sent earlier

    Quick Start (Docker Compose)

    Install Docker following the official guide. You may have to uninstall Docker if you installed it using a different guide.

    Clone repository and enter directory:

    git clone git@github.com:Cameri/nostream.git
    cd nostream
    

    Generate a secret with: openssl rand -hex 128 Copy the output and paste it into an .env file:

    SECRET=aaabbbccc...dddeeefff
    # Secret shortened for brevity
    

    Start:

    ./scripts/start
    

    or

    ./scripts/start_with_tor
    

    Stop the server with:

    ./scripts/stop
    

    Print the Tor hostname:

    ./scripts/print_tor_hostname
    

    Running as a Service

    By default this server will run continuously until you stop it with Ctrl+C or until the system restarts.

    You can install as a systemd service if you want the server to run again automatically whenever the system is restarted. For example:

    $ nano /etc/systemd/system/nostream.service
    
    # Note: replace "User=..." with your username, and
    # "/home/nostr/nostream" with the directory where you cloned the repo.
    
    [Unit]
    Description=Nostr TS Relay
    After=network.target
    StartLimitIntervalSec=0
    
    [Service]
    Type=simple
    Restart=always
    RestartSec=5
    User=nostr
    WorkingDirectory=/home/nostr/nostream
    ExecStart=/home/nostr/nostream/scripts/start
    ExecStop=/home/nostr/nostream/scripts/stop
    
    [Install]
    WantedBy=multi-user.target
    

    And then:

    systemctl enable nostream
    systemctl start nostream
    

    The logs can be viewed with:

    journalctl -u nostream
    

    Quick Start (Standalone)

    Set the following environment variables:

    DB_URI="postgresql://postgres:postgres@localhost:5432/nostr_ts_relay_test"
    DB_USER=postgres
    

    or

    DB_HOST=localhost
    DB_PORT=5432
    DB_NAME=nostr_ts_relay
    DB_USER=postgres
    DB_PASSWORD=postgres
    
    REDIS_URI="redis://default:nostr_ts_relay@localhost:6379"
    
    REDIS_HOST=localhost
    REDIS_PORT=6379
    REDIS_USER=default
    REDIS_PASSWORD=nostr_ts_relay
    

    Generate a long random secret and set SECRET: You may want to use openssl rand -hex 128 to generate a secret.

    SECRET=aaabbbccc...dddeeefff
    # Secret shortened for brevity
    

    Initializing the database

    Create nostr_ts_relay database:

    $ psql -h $DB_HOST -p $DB_PORT -U $DB_USER -W
    postgres=# create database nostr_ts_relay;
    postgres=# quit
    

    Start Redis and use redis-cli to set the default password and verify:

    $ redis-cli
    127.0.0.1:6379> CONFIG SET requirepass "nostr_ts_relay"
    OK
    127.0.0.1:6379> AUTH nostr_ts_relay
    Ok
    

    Clone repository and enter directory:

    git clone git@github.com:Cameri/nostream.git
    cd nostream
    

    Install dependencies:

    npm install -g knex
    npm install
    

    Run migrations (at least once and after pulling new changes):

    NODE_OPTIONS="-r dotenv/config" npm run db:migrate
    

    Create .nostr folder inside nostream project folder and copy over the settings file:

    mkdir .nostr
    cp resources/default-settings.yaml .nostr/settings.yaml
    

    To start in development mode:

    npm run dev
    

    Or, start in production mode:

    npm run start
    

    To clean up the build, coverage and test reports run:

    npm run clean
    

    Development Quick Start (Docker Compose)

    Install Docker Desktop following the official guide. You may have to uninstall Docker on your machine if you installed it using a different guide.

    Clone repository and enter directory:

    git clone git@github.com:Cameri/nostream.git
    cd nostream
    

    Start:

    ./scripts/start_local
    

    This will run in the foreground of the terminal until you stop it with Ctrl+C.

    Tests

    Unit tests

    Open a terminal and change to the project's directory:

    cd /path/to/nostream
    

    Run unit tests with:

    npm run test:unit
    

    Or, run unit tests in watch mode:

    npm run test:unit:watch
    

    To get unit test coverage run:

    npm run cover:unit
    

    To see the unit tests report open .test-reports/unit/index.html with a browser:

    open .test-reports/unit/index.html
    

    To see the unit tests coverage report open .coverage/unit/lcov-report/index.html with a browser:

    open .coverage/unit/lcov-report/index.html
    

    Integration tests (Docker Compose)

    Open a terminal and change to the project's directory:

    cd /path/to/nostream
    

    Run integration tests with:

    npm run docker:test:integration
    

    And to get integration test coverage run:

    npm run docker:cover:integration
    

    Integration tests (Standalone)

    Open a terminal and change to the project's directory:

    cd /path/to/nostream
    

    Set the following environment variables:

    DB_URI="postgresql://postgres:postgres@localhost:5432/nostr_ts_relay_test"
    
    or
    
    DB_HOST=localhost
    DB_PORT=5432
    DB_NAME=nostr_ts_relay_test
    DB_USER=postgres
    DB_PASSWORD=postgres
    DB_MIN_POOL_SIZE=1
    DB_MAX_POOL_SIZE=2
    

    Then run the integration tests:

    npm run test:integration
    

    To see the integration tests report open .test-reports/integration/report.html with a browser:

    open .test-reports/integration/report.html
    

    To get the integration test coverage run:

    npm run cover:integration
    

    To see the integration test coverage report open .coverage/integration/lcov-report/index.html with a browser.

    open .coverage/integration/lcov-report/index.html
    

    Configuration

    You can change the default folder by setting the NOSTR_CONFIG_DIR environment variable to a different path.

    Run nostream using one of the quick-start guides at least once and nostream/.nostr/settings.json will be created. Any changes made to the settings file will be read on the next start.

    Default settings can be found under resources/default-settings.yaml. Feel free to copy it to nostream/.nostr/settings.yaml if you would like to have a settings file before running the relay first.

    See CONFIGURATION.md for a detailed explanation of each environment variable and setting.

    Dev Channel

    For development discussions, please use the Nostr Typescript Relay Dev Group.

    For discussions about the protocol, please feel free to use the Nostr Telegram Group.

    Author

    I'm Cameri on most social networks. You can find me on Nostr by npub1qqqqqqyz0la2jjl752yv8h7wgs3v098mh9nztd4nr6gynaef6uqqt0n47m.

    from https://github.com/Cameri/nostream 

    (https://github.com/nostr-protocol/nostr)

    --------------------------------------------------

    nostr 是一个相当简单的开放协议,可以在全球范围内搭建一个有趣的社交网络。因为协议本身很简单,所以大部分的加密、签名工作在客户端完成,而中继服务器 Relay 所做的事情只是接收事件消息,根据客户端订阅要求返回消息。

    目前 relay 的实现也很多,比如 nostream 的搭建就很方便,只要安装好 docker,设置好域名,直接运行就可以啦。

    以下记录一下 Relay 的搭建过程。

    1. 安装基础软件

    首先分配一台安装 Ubuntu Server 20.04 LTS Ubuntu Server 20.04 LTS 操作系统的虚拟机,默认规格就够了。

    首先,更新软件包列表。提示 30 个软件包需要升级,暂时先不管。

    kimim@nostr:~$ sudo apt update
    
    # update package list from mirrors
    Fetched 25.5 MB in 4s (6240 kB/s)
    Reading package lists... Done
    Building dependency tree... Done
    Reading state information... Done
    30 packages can be upgraded. Run 'apt list --upgradable' to see them.
    

    然后,安装以下软件包,用来运行编译 nostream,配置 relay 的证书。

    kimim@nostr:~$ sudo apt install nodejs npm nginx certbot python3-certbot-nginx
    
    # list of packages need to be installed
    0 upgraded, 410 newly installed, 0 to remove and 30 not upgraded.
    Need to get 168 MB of archives.
    After this operation, 669 MB of additional disk space will be used.
    Do you want to continue? [Y/n] Y
    # installation in progress
    

    2. 安装 docker

    接下来,安装 docker,因为根据 nostream 官网要求,需要安装官方的 docker。就是按照 https://docs.docker.com/engine/install/ubuntu/ 安装 docker。

    首先,删除已经安装的 docker(如果有,我的这台机器刚初始化,所以没有安装 docker)。

    kimim@nostr:~$ sudo apt-get remove docker.io
    Reading package lists... Done
    Building dependency tree... Done
    Reading state information... Done
    Package 'docker.io' is not installed, so not removed
    0 upgraded, 0 newly installed, 0 to remove and 30 not upgraded.
    

    然后,安装 docker 需要的证书组件。

    kimim@nostr:~$ sudo apt-get install ca-certificates curl gnupg lsb-release
    Reading package lists... Done
    Building dependency tree... Done
    Reading state information... Done
    lsb-release is already the newest version (11.1.0ubuntu4).
    lsb-release set to manually installed.
    ca-certificates is already the newest version (20211016ubuntu0.22.04.1).
    ca-certificates set to manually installed.
    curl is already the newest version (7.81.0-1ubuntu1.7).
    curl set to manually installed.
    gnupg is already the newest version (2.2.27-3ubuntu2.1).
    gnupg set to manually installed.
    0 upgraded, 0 newly installed, 0 to remove and 30 not upgraded.
    

    接着,添加 docker 官方 GPG 证书。

    kimim@nostr:~$ sudo mkdir -p /etc/apt/keyrings
    kimim@nostr:~$ curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
    

    添加 docker 官方源:

    kimim@nostr:~$ echo \
      "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
      $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
    

    再次更新软件包:

    kimim@nostr:~$ sudo apt-get update
    Get:1 https://download.docker.com/linux/ubuntu jammy InRelease [48.9 kB]
    Get:2 https://download.docker.com/linux/ubuntu jammy/stable amd64 Packages [12.7 kB]
    Fetched 61.6 kB in 1s (105 kB/s)
    Reading package lists... Done
    

    安装 docker 以及 cli,compose 插件。

    kimim@nostr:~$ sudo apt-get install docker-ce docker-ce-cli containerd.io docker-compose-plugin
    
    # list of packages need to be installed
    0 upgraded, 11 newly installed, 0 to remove and 30 not upgraded.
    Need to get 111 MB of archives.
    After this operation, 397 MB of additional disk space will be used.
    Do you want to continue? [Y/n] Y
    
    # installation in progress
    

    把当前用户添加到 docker 权限组:

    kimim@nostr:~$ sudo usermod -a -G docker kimim
    kimim@nostr:~$ newgrp docker
    

    测试一下 docker 是否正常工作:

    kimim@nostr:~$ docker run hello-world
    Unable to find image 'hello-world:latest' locally
    latest: Pulling from library/hello-world
    2db29710123e: Pull complete
    Digest: sha256:aa0cc8055b82dc2509bed2e19b275c8f463506616377219d9642221ab53cf9fe
    Status: Downloaded newer image for hello-world:latest
    
    Hello from Docker!
    This message shows that your installation appears to be working correctly.
    

    3. 配置服务器证书

    修改 nginx 的配置文件。请根据自己的服务器域名修改 server_name 字段。

    kimim@nostr:~$ sudo rm -rf /etc/nginx/sites-available/default
    kimim@nostr:~$ sudo vi /etc/nginx/sites-available/default
    # 添加以下内容
    kimim@nostr:~$ cat /etc/nginx/sites-available/default
    server{
        server_name nostr.kimi.im;
        location / {
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header Host $host;
            proxy_pass http://127.0.0.1:8008;
            proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection "upgrade";
        }
    }
    

    重启 nginx,使配置生效。

    kimim@nostr:~$ sudo service nginx restart
    

    添加 A 域名记录,比如:

    TYPE    HOST            ANSWER          TTL
    A       nostr.kimi.im   192.30.252.153  300
    

    等一两分钟,等域名记录生效,再用 certbox 配置服务器证书:

    kimim@nostr:~$ sudo certbot --nginx -d nostr.kimi.im
    Saving debug log to /var/log/letsencrypt/letsencrypt.log
    Enter email address (used for urgent renewal and security notices)
     (Enter 'c' to cancel): kimim@kimi.im
    
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    Please read the Terms of Service at
    https://letsencrypt.org/documents/LE-SA-v1.3-September-21-2022.pdf. You must
    agree in order to register with the ACME server. Do you agree?
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    (Y)es/(N)o: Y
    
    Successfully received certificate.
    Certificate is saved at: /etc/letsencrypt/live/nostr.kimi.im/fullchain.pem
    Key is saved at:         /etc/letsencrypt/live/nostr.kimi.im/privkey.pem
    This certificate expires on 2023-05-05.
    These files will be updated when the certificate renews.
    Certbot has set up a scheduled task to automatically renew this certificate in the background.
    
    Deploying certificate
    Successfully deployed certificate for nostr.kimi.im to /etc/nginx/sites-enabled/default
    Congratulations! You have successfully enabled HTTPS on https://nostr.kimi.im
    
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    If you like Certbot, please consider supporting our work by:
     * Donating to ISRG / Let's Encrypt:   https://letsencrypt.org/donate
     * Donating to EFF:                    https://eff.org/donate-le
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    

    4. 运行 nostream

    获取 nostream 代码:

    kimim@nostr:~$ git clone https://github.com/Cameri/nostream.git
    Cloning into 'nostream'...
    remote: Enumerating objects: 3287, done.
    remote: Counting objects: 100% (1228/1228), done.
    remote: Compressing objects: 100% (415/415), done.
    remote: Total 3287 (delta 889), reused 984 (delta 803), pack-reused 2059
    Receiving objects: 100% (3287/3287), 1.05 MiB | 7.90 MiB/s, done.
    Resolving deltas: 100% (2036/2036), done.
    

    用 docker 运行 nostream,大概用了两分钟,就看到一个漂亮的 NOSTREAM logo 了。

    kimim@nostr:~$ cd nostream/
    kimim@nostr:~/nostream$ npm run docker:compose:start
    > nostream@1.21.0 docker:compose:start
    > ./scripts/start
    
    [+] Running 25/25
    # ...
    [+] Building 128.2s (16/16) FINISHED
    # ...
    [+] Running 6/5
    # ...
    Attaching to nostream, nostream-cache, nostream-db, nostream-migrate
    # ...
    nostream-db       | CREATE DATABASE
    # ...
    nostream          |
    nostream          |  ███▄    █  ▒█████    ██████ ▄▄▄█████▓ ██▀███  ▓█████ ▄▄▄       ███▄ ▄███▓
    nostream          |  ██ ▀█   █ ▒██▒  ██▒▒██    ▒ ▓  ██▒ ▓▒▓██ ▒ ██▒▓█   ▀▒████▄    ▓██▒▀█▀ ██▒
    nostream          | ▓██  ▀█ ██▒▒██░  ██▒░ ▓██▄   ▒ ▓██░ ▒░▓██ ░▄█ ▒▒███  ▒██  ▀█▄  ▓██    ▓██░
    nostream          | ▓██▒  ▐▌██▒▒██   ██░  ▒   ██▒░ ▓██▓ ░ ▒██▀▀█▄  ▒▓█  ▄░██▄▄▄▄██ ▒██    ▒██
    nostream          | ▒██░   ▓██░░ ████▓▒░▒██████▒▒  ▒██▒ ░ ░██▓ ▒██▒░▒████▒▓█   ▓██▒▒██▒   ░██▒
    nostream          | ░ ▒░   ▒ ▒ ░ ▒░▒░▒░ ▒ ▒▓▒ ▒ ░  ▒ ░░   ░ ▒▓ ░▒▓░░░ ▒░ ░▒▒   ▓▒█░░ ▒░   ░  ░
    nostream          | ░ ░░   ░ ▒░  ░ ▒ ▒░ ░ ░▒  ░ ░    ░      ░▒ ░ ▒░ ░ ░  ░ ▒   ▒▒ ░░  ░      ░
    nostream          |    ░   ░ ░ ░ ░ ░ ▒  ░  ░  ░    ░        ░░   ░    ░    ░   ▒   ░      ░
    nostream          |          ░     ░ ░        ░              ░        ░  ░     ░  ░       ░
    nostream          |                                   v1.21.0
    nostream          |           NIPs implemented: 1,2,4,9,11,12,15,16,20,22,26,28,33,40
    nostream          |                            Pay-to-relay disabled
    nostream          |                          Payments provider: zebedee
    nostream          |                           2 client workers started
    nostream          |                         1 maintenance worker started
    nostream          |                         Tor hidden service: disabled
    

    5. 用 noscl 测试 relay 是否正常工作

    删除之前添加的 relay:

    [kimim@virtualbox Desktop]$ ./noscl relay
    wss://nos.lol: rw
    [kimim@virtualbox Desktop]$ ./noscl relay remove wss://nos.lol
    Removed relay wss://nos.lol.
    

    添加刚刚搭建的 relay:

    [kimim@virtualbox Desktop]$ ./noscl relay add wss://nostr.kimi.im
    Added relay wss://nostr.kimi.im.
    

    发送消息 Bonjour tout le monde:

    [kimim@virtualbox Desktop]$ ./noscl publish "Bonjour tout le monde"
    Sent event a170e3f3c4f8cfb79cacccf28d5f7d51a5e17b4e40c0984593a692b83f9cf43c to 'wss://nostr.kimi.im'.
    Seen a170e3f3c4f8cfb79cacccf28d5f7d51a5e17b4e40c0984593a692b83f9cf43c on 'wss://nostr.kimi.im'.
    

    在 Damus 一开始收不测试帐号发送的消息,需要在 Damus 客户端也添加同样的 relay。

    然后就能看到测试帐号发的:Bonjour tout le monde

     

     

     


     

    No comments:

    Post a Comment