A nostr relay written in Rust with support for the entire relay protocol and data persistence using PostgreSQL or SQLite.
{% hint style="warning" %}
Difficulty: Medium

Nostr is a straightforward and open protocol for global, decentralized, and censorship-resistant social media. It offers numerous advantages for users and is completely free, requiring no ID or third-party verification to begin connecting with like-minded individuals and expanding your community. While nostr is sometimes confused as just another social media platform, it goes beyond that. Explore the resources provided here to discover their significant potential.
This protocol is based on relays. Relays are servers that can be operated by anyone. By opening a persistent connection with the server, clients (or apps) can push and pull events in real-time.
Relays are the central element of the nostr protocol, responsible for storing events received from clients.
Crucially, relays do not communicate with each other. Only the relays you're connected to will receive and store your events. This is a key feature of nostr, emphasizing the lack of communication between relays. Therefore, you should connect to as many relays as you wish to share your data with.
Clients should always provide users the flexibility to connect to multiple relays. Users can also decide whether they want to read, write, or do both with the relays they're connected to. This means I can choose to connect to a specific relay to access content without necessarily sharing my own events there, or vice versa.
You can obtain more info about nostr on these additional resources:
admin, update and upgrade your OSsudo apt update && sudo apt full-upgrade
sudo apt install build-essential cmake protobuf-compiler pkg-config libssl-dev
{% hint style="info" %}
If you want to use the default SQLite database backend, go to the Use the default SQLite database backend extra section to install the additional SQLite dependency packages
rustc --version
Example of expected output:
rustc 1.71.0 (8ede3aae2 2023-07-12)
cargo -V
Example of expected output:
cargo 1.71.0 (cfd3bbd8f 2023-06-08)
{% hint style="info" %}
If you obtain "command not found" output, you need to follow the Rustup + Cargo bonus guide to install it and then come back to continue with the guide
{% hint style="info" %}
Skip this step if you want to use the SQLite database; go directly to the next section
psql -V
Example of expected output:
psql (PostgreSQL) 15.3 (Ubuntu 15.3-1.pgdg22.04+1)
{% hint style="info" %}
If you obtain "command not found" outputs, you need to follow the PostgreSQL bonus guide to install it and then come back to continue with the guide
admin, create a new database with the postgres user and assign it as the owner to the admin usersudo -u postgres createdb -O admin nostrelay
admin, go to the temporary foldercd /tmp
VERSION=0.9.0
nostr-rs-relay foldergit clone --branch $VERSION https://github.com/scsibug/nostr-rs-relay.git && cd nostr-rs-relay
cargo build --release
Updating crates.io index
Downloaded pathdiff v0.2.1
Downloaded num_cpus v1.16.0
Downloaded indexmap v2.0.0
Downloaded parking_lot_core v0.9.8
Downloaded want v0.3.1
Downloaded pest v2.7.2
Downloaded percent-encoding v2.3.0
Downloaded parse_duration v2.1.1
Downloaded prost-build v0.11.9
Downloaded clap_lex v0.5.0
Downloaded autocfg v0.1.8
Downloaded fastrand v2.0.0
Downloaded is-terminal v0.4.9
Downloaded json5 v0.4.1
Downloaded num v0.2.1
Downloaded paste v1.0.14
Downloaded pin-project-internal v1.1.3
Downloaded num-iter v0.1.43
Downloaded fallible-streaming-iterator v0.1.9
Downloaded md-5 v0.10.5
Downloaded linked-hash-map v0.5.6
Downloaded number_prefix v0.4.0
Downloaded itoa v1.0.9
Downloaded openssl-sys v0.9.91
Downloaded async-lock v2.7.0
Downloaded pest_derive v2.7.2
Downloaded async-channel v1.9.0
Downloaded tokio-io-timeout v1.2.0
Downloaded async-global-executor v2.3.1
Downloaded sync_wrapper v0.1.2
Downloaded matchers v0.1.0
Downloaded no-std-compat v0.4.1
Downloaded block-padding v0.3.3
Downloaded pest_generator v2.7.2
Downloaded atomic-waker v1.1.1
Downloaded pin-project-lite v0.2.12
[...]
{% hint style="info" %}
If the prompt shows you this error:
error: rustup could not choose a version of cargo to run, because one wasn't specified explicitly, and no default is configured. help: run 'rustup default stable' to download the latest stable release of Rust and set it as your default toolchain.
You need to type rustup default stable and wait for the process to finish, then try the command again before
{% hint style="info" %}
This process can take quite a long time, 10-15 minutes or more, depending on the performance of your device. Please be patient until the prompt shows again
sudo install -m 0755 -o root -g root -t /usr/local/bin /tmp/nostr-rs-relay/target/release/nostr-rs-relay
nostr-rs-relay -V
Example of expected output:
nostr-rs-relay 0.8.9
{% hint style="info" %}
If you come to update, this is the final step. Continue with the indications of the Upgrade section
nostr with this commandsudo adduser --gecos "" --disabled-password nostr
Expected output:
Adding user `nostr' ...
Adding new group `nostr' (1007) ...
Adding new user `nostr' (1007) with group `nostr' ...
Creating home directory `/home/nostr' ...
Copying files from `/etc/skel' ...
nostr user foldersudo su - nostr
favicon.ico file, download it by entering this command, if not, download your own, or skip this step, not to provide any (remember to leave thefavicon.icocommented on the configuration file)wget https://raw.githubusercontent.com/minibolt-guide/minibolt/main/resources/favicon.ico
rs-relay foldermkdir rs-relay
admin userexit
sudo cp /tmp/nostr-rs-relay/config.toml /home/nostr/rs-relay/
nostr usersudo chown nostr:nostr /home/nostr/rs-relay/config.toml
nostr-rs-relay folder to be ready for the next updatesudo rm -r /tmp/nostr-rs-relay
sudo nano /home/nostr/rs-relay/config.toml
Customize this with your own info (*):
(*) Click on parameter to get an example/explanation
{% hint style="info" %}
If you don't have a pubkey generated yet, you can follow the Create your nostr key pair section and then continue with this
You can use this tool to convert your "npub" pubkey to hexadecimal format
{% hint style="info" %}
If you want to use the default SQLite database backend, pay attention to not including the next lines (do not uncomment):
engine = "postgres"
connection = "postgresql://admin:admin@localhost:5432/nostrelay"
Uncomment and replace only the next line:
data_directory = "/data/nostr/rs-relay/db"
-> More details and additional steps in the exclusive extra section
Required same as next (*):
(*) click on the parameter to get action to do (<Edit> or <Uncomment>)
connection = "postgresql://admin:admin@localhost:5432/nostrelay"[3:3]
{% hint style="info" %}
If you want, use the same favicon.ico file downloaded before (the relay's icon of MiniBolt) and the value relay_icon parameter (URL -> https://blossom.minibolt.info/35cb7871786875878269f04faafd3be8b5a536b9c4ce5f4bbbf82742873bc222.png), or replace it with your info
The system needs to run the nostr relay daemon automatically in the background, even when nobody is logged in. We use systemd, a daemon that controls the startup process using configuration files.
admin, create the service filesudo nano /etc/systemd/system/nostr-relay.service
# MiniBolt: systemd unit for nostr relay
# /etc/systemd/system/nostr-relay.service
[Unit]
Description=Nostr relay
Requires=network-online.target postgresql.service
After=network-online.target postgresql.service
[Service]
WorkingDirectory=/home/nostr
ExecStart=/usr/local/bin/nostr-rs-relay -c /home/nostr/rs-relay/config.toml
Environment=RUST_LOG=info,nostr_rs_relay=info
User=nostr
Group=nostr
# Process management
####################
Type=simple
TimeoutStopSec=10
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
sudo systemctl enable nostr-relay
journalctl -fu nostr-relay
To keep an eye on the software movements, start your SSH program (eg. PuTTY) a second time, connect to the MiniBolt node, and log in as admin
sudo systemctl start nostr-relay
journalctl -fu nostr-relay ⬇️Jun 07 10:49:45 minibolt systemd[1]: Started Nostr relay.
Jun 07 10:49:45 minibolt nostr-rs-relay[2604788]: 2024-06-07T10:49:45.130712Z INFO nostr_rs_relay: Starting up from main
Jun 07 10:49:45 minibolt nostr-rs-relay[2604788]: 2024-06-07T10:49:45.150378Z INFO nostr_rs_relay::server: listening on: 127.0.0.1:8880
Jun 07 10:49:45 minibolt nostr-rs-relay[2604788]: 2024-06-07T10:49:45.302830Z INFO sqlx::postgres::notice: relation "migrations" already exists, skipping
Jun 07 10:49:45 minibolt nostr-rs-relay[2604788]: 2024-06-07T10:49:45.337047Z INFO nostr_rs_relay::db: Postgres migration completed, at v5
Jun 07 10:49:45 minibolt nostr-rs-relay[2604788]: 2024-06-07T10:49:45.337902Z INFO nostr_rs_relay::server: db writer created
Jun 07 10:49:45 minibolt nostr-rs-relay[2604788]: 2024-06-07T10:49:45.337945Z INFO nostr_rs_relay::server: control message listener started
Jun 07 10:49:45 minibolt nostr-rs-relay[2604788]: 2024-06-07T10:49:45.338012Z INFO nostr_rs_relay::server: reading favicon...
[...]
8880 portsudo ss -tulpn | grep nostr-rs-relay
Expected output:
tcp LISTEN 0 128 127.0.0.1:8880 0.0.0.0:* users:(("nostr-rs-relay",pid=138820,fd=24))
-> 3 different methods ⬇️
<relay.domain.com> with your Nostr relay URL: https://legacy.nostr.watch/relay/relay.domain.com, example: https://legacy.nostr.watch/relay/relay.damus.ioExpected output:
{% tab title="Method 2" %}
Go to the websocketking.com website, type in the WebSocket URL box your Nostr relay URL e.g. wss://relay.domain.com, and click on the [Connect] button
Example of expected output:
{% tab title="Method 3" %}
Go to the https://nostrdebug.com/relay website, type in the box your Nostr relay URL, e.g. wss://relay.domain.com, and click on the [Connect] button. You should see the status "✅ Connected" in the history
admin, install the next additional dependenciessudo apt install sqlite3 libsqlite3-dev
rs-relay and db foldermkdir -p /data/nostr/rs-relay/db
sudo cp /tmp/nostr-rs-relay/config.toml /data/nostr/rs-relay/db
nostr usersudo chown -R nostr:nostr /data/nostr
sudo nano /data/nostr/rs-relay/config.toml
data_directory = "/data/nostr/rs-relay/db"
{% hint style="info" %}
Ignore the next lines related to PostgreSQL (do not uncomment or edit):
engine = "postgres"
connection = "postgresql://admin:admin@localhost:5432/nostrelay"
postgres.service of the systemd service linesRequires=network-online.target
After=network-online.target
{% hint style="info" %}
Continue with the guide, the rest of the steps are the same as PostgreSQL use
{% hint style="warning" %}
Select a strong password for the Alby extension (this password is for encrypting your future Nostr private key and possible funds of the integrated LN wallet)



{% hint style="info" %}
You will see the nostr public & private keys in the property section:
{% hint style="info" %}
Click on the [Nostr Settings] box to obtain your private key and backup on your password manager app; you will need it for mobile clients (e.g. Amethyst), where you will need to enter it manually. Example:
{% hint style="info" %}
If you see this banner when you enter the "Nostr Settings" section, this means that you should backup carefully the private key, because the existing seeds that you have are not compatible with Alby, only the private key
{% hint style="info" %}
You will see the Nostr Public key in the property section, check if correct:
{% hint style="info" %}
Click on the [Nostr Settings] box to obtain your private key if you haven't yet:
{% hint style="success" %}
Now, you can use Alby to log in to compatible web clients using NIP-07 [Login from extension]
{% hint style="info" %}
If you prefer to generate your key pair, you can mine them using the Rana tool and the Minibolt node.
Be careful when doing this, as it will use all the available resources of the machine and could render other important applications you are running unusable. Gracefully shutdown them before starting this process
{% tabs %}
{% tab title="Coracle" %}
Coracle is a web client for the Nostr protocol focused on pushing the boundaries of what's unique about Nostr, including relay selection and management, web-of-trust based moderation and content recommendations, and privacy protection.
{% tab title="Snort" %}
A nostr UI built with React aiming for speed and efficiency.
{% tab title="Zap stream" %}
Nostr live streaming.
{% tab title="Rana" %}
Nostr public key mining tool.
{% tab title="Nosy" %}
Find the top relays of those who follow you or you follow.
{% tabs %}
{% tab title="Nostree" %}
A Nostr-based application to create, manage, and discover link lists, show notes, and other stuff.
{% tab title="Highlighter" %}
Discover and share curated insights by people you trust.
Highlight, share, discover, comment, and earn on any text via the nostr network. Books, articles, tweets, anything!
{% tab title="Pinstr" %}
Pinstr is a decentralized, free, and open-source social network built on top of the Nostr Protocol for curating and sharing interests with the world.
{% tab title="Nostr nests" %}
Nostr Nests is an audio space for chatting, brainstorming, debating, jamming, micro-conferences, and more.
{% tabs %}
{% tab title="Pleb.to" %}
Pleb.to does NOSTR things... documents, links, graphs, maps, and more... Pleb.to is a portal to your decentralized data.
{% tab title="Nostrudel" %}
"My half-baked personal nostr client".
{% tab title="Habla.news" %}
Habla allows you to read, write, curate, and monetize long-form content over Nostr, a censorship-resistant protocol for social media that uses long-form nostr content.
{% tab title="Amethyst" %}
Nostr client for Android.
Amethyst brings the best social network to your Android phone.
{% tabs %}
{% tab title="Password Manager (Vault)" %}
A free, open-source, and decentralized password manager, powered by NOSTR.
Chrome-based extension | GitHub
{% tab title="njump" %}
njump is a HTTP Nostr gateway that allows you to browse profiles, notes, and relays; it is an easy way to preview a resource and then open it with your preferred client.
{% tab title="exit.pub" %}
Tool for migrating your entire past Twitter activity to Nostr.
If you want all your past events to be accessible through your new relay, you can back them up by following these instructions:
[wss://relay.domain.com] address to the list of preferred relays in your profile (in the empty box below), select the read+write option, and click the [Update] button.You can take the opportunity to add more preferred relays to your profile to also push events to them, selected from this list

{% hint style="info" %}
Please wait patiently until all processes are finished. This might take some time, depending on the number of events you've published on Nostr with that pubkey, meaning the interactions you've had on Nostr.
Optionally, you can save a copy of all your events locally, as a banner will appear after fetching the events from the relays. These can be restored later using the [Restore] button and broadcasting again to the relays.
To use your Nostr relay when you're on the go, you can easily create a Tor hidden service.
admin, edit the torrc filesudo nano +63 /etc/tor/torrc --linenumbers
## This section is just for location-hidden services ##" in the torrc file. Save and exit# Hidden Service Nostr relay
HiddenServiceDir /var/lib/tor/hidden_service_nostr_relay/
HiddenServiceEnableIntroDoSDefense 1
HiddenServicePoWDefensesEnabled 1
HiddenServicePort 80 127.0.0.1:8880
sudo systemctl reload tor
sudo cat /var/lib/tor/hidden_service_nostr_relay/hostname
Expected output:
abcdefg..............xyz.onion
{% hint style="info" %}
You should now be able to connect to your Nostr relay remotely via Tor using your hostname. eg: ws://abcdefg..............xyz.onion
This is necessary to access you ws:// URL, since Tor does not use wss:// due to the Tor network being encrypted by design
about:config in the address bar. Press the button: "Accept the Risk and Continue" when it shows you.network.websocket.allowInsecureFromHTTPS on the search barnetwork.websocket.allowInsecureFromHTTPS to true
You may want to expose your Nostr relay publicly using a clearnet address. To do this, follow the next steps:

Select the CNAME type on the drop down
Type the selected subdomain (i.e service name "relay") as the Name field
Type the tunnel
<UUID>of your previously obtained in the Create a tunnel and give it a name section as the Target field
Ensure you enable the switch on the
Proxy statusfield to be "Proxied"
Click on the [Save] button to save the new DNS registry
If you didn't follow before, continue with the "Configuration" section of the Cloudflare tunnel guide to Increase the maximum UDP Buffer Sizes and Create systemd service
Edit theconfig.yml
sudo nano /home/admin/.cloudflared/config.yml
config.yml# Nostr relay
- hostname: <subdomain>.<domain.com>
service: http://localhost:8880
You can choose the subdomain you want; the above information is an example, but keep in mind to use the port
8880and always maintaining the "- service: http_status:404" line at the end of the file
sudo systemctl restart cloudflared
{% hint style="info" %}
Try to access the newly created public access to the service by going to the wss://<subdomain>. <domain.com>, i.e, wss://relay.domain.com
Security submenu -> Click on Security rules -> Create Rule -> Custom rules
[Save] button to apply changesDisplay the related log events to ensure this rule is being applied correctly:
1. On the Security submenu -> Click on Analytics -> Events (Tab)
[+ Add filter] button:
Host -> equals -> <relay.domain.com> click on [Apply]
{% hint style="info" %}
Replace <relay.domain.com> to your proper subdomain relay
[+ Add filter] button: On the dropdown Action -> equals -> Skip click on [Apply]
Example of expected output:

Sampled logs
admin, stop nostr-rs-relay servicesudo systemctl stop nostr-relay
config.toml file with the new one of the new version (if needed){% hint style="warning" %}
This step is only necessary if you see changes on the config file template from your current version until the current release (not common), you can display this on this history link. If there are no changes, jump directly to the next "Start nostr-rs-relay service again" >sudo systemctl start nostr-relay step
Here are 2 cases depending on your chosen database backend:
config.toml file to keep a copy of your old configurationsudo cp /home/nostr/rs-relay/config.toml /home/nostr/rs-relay/config.toml.backup
nostr usersudo chown nostr:nostr /home/nostr/rs-relay/config.toml.backup
config.toml file with the new versionsudo cp /tmp/nostr-rs-relay/config.toml /home/nostr/rs-relay/
sudo nano /home/nostr/rs-relay/config.toml
config.toml file to keep a copy of your old configurationsudo cp /data/nostr/rs-relay/config.toml /data/nostr/rs-relay/config.toml.backup
nostr usersudo chown nostr:nostr /data/nostr/rs-relay/config.toml.backup
config.toml file with the new versionsudo cp /tmp/nostr-rs-relay/config.toml /data/nostr/rs-relay/
sudo nano /data/nostr/rs-relay/config.toml
nostr-rs-relay service againsudo systemctl start nostr-relay
nostr-rs-relay folder to be ready for the next updatesudo rm -r /tmp/nostr-rs-relay
admin, stop nostr-relaysudo systemctl stop nostr-relay
sudo systemctl disable nostr-relay
nostr-relay servicesudo rm /etc/systemd/system/nostr-relay.service
userdel: nostr mail spool (/var/mail/nym) not found output, the uninstall has been successfulsudo userdel -rf nostr
sudo rm -r /data/nostr/rs-relay
nostrelay databasesudo -u postgres psql -c "DROP DATABASE nostrelay;"
admin, edit config.ymlnano /home/admin/.cloudflared/config.yml
# MiniBolt: cloudflared configuration
# /home/admin/.cloudflared/config.yml
tunnel: <UUID>
credentials-file: /home/admin/.cloudflared/<UUID>.json
ingress:
# Nostr relay
# - hostname: relay.<domain.com>
# service: ws://localhost:8880
- service: http_status:404
sudo systemctl restart cloudflared
admin, delete the nostr-rs-relay binary of the systemsudo rm /usr/local/bin/nostr-rs-relay
| Port | Protocol | Use |
|---|---|---|
| 8880 | TCP | Default port |