
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.
NIPs with a relay-specific implementation are listed here.
created_at LimitsWARNING: Docker distributions from Snap, Brew or Debian repositories are NOT SUPPORTED and will result in errors. Install Docker from their official guide ONLY.
.env file.nostr/settings.yaml file make the following changes:payments.enabled to truepayments.feeSchedules.admission.enabled to truelimits.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, nwc
Set ZEBEDEE_API_KEY environment variable with the API Key above on your .env file
ZEBEDEE_API_KEY={YOUR_ZEBEDEE_API_KEY_HERE}
Follow the required steps for all payments processors
.nostr/settings.yaml file make the following changes:payments.processor to zebedeepaymentsProcessors.zebedee.callbackBaseURL to match your Nostream URL (e.g. https://{YOUR_DOMAIN_HERE}/callbacks/zebedee)nostream stop followed by nostream start)Read the in-depth guide for more information: Set Up a Paid Nostr Relay with ZEBEDEE API
https://{YOUR_DOMAIN_HERE}/callbacks/nodeless) and make sure to enable all of the events. Grab the generated store webhook secretSet 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:
payments.processor to nodelesspaymentsProcessors.nodeless.storeId to your store IDRestart Nostream (nostream stop followed by nostream start)
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:
payments.processor to opennodeRestart Nostream (nostream stop followed by nostream start)
LNBITS_API_KEY environment variable with the Invoice/read key Key above on your .env fileLNBITS_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 (nostream stop followed by nostream start)
Alby or any LNURL Provider with LNURL-verify support
.nostr/settings.yaml file make the following changes:payments.processor to lnurllnurl.invoiceURL to your LNURL (e.g. https://getalby.com/lnurlp/your-username)nostream stop followed by nostream start)Nostr Wallet Connect (NIP-47 / NWC)
NWC_URL environment variable on your .env fileNWC_URL={NOSTR_WALLET_CONNECT_URL}
.nostr/settings.yaml file make the following changes:payments.processor to nwc./scripts/stop followed by ./scripts/start)Ensure payments are required for your public key
["REQ", "payment-test", {"authors":["your-pubkey-in-hexadecimal"]}]For full command reference and interactive mode documentation, see CLI.md.
Non-interactive CLI usage conventions:
- exit 0 on success
- exit 1 on runtime/validation errors
- exit 2 on usage errors (invalid command/options)
Optional global installation from a source checkout:
pnpm add -g .
nostream --help
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:
nostream start
or
nostream start --tor
or
nostream start --i2p
or
RELAY_DOMAIN=relay.example.com CERTBOT_EMAIL=you@example.com nostream start --nginx
Stop the server with:
nostream stop
Print the Tor hostname:
nostream info --tor-hostname
Print I2P hostname(s):
nostream info --i2p-hostname
The old shell wrapper scripts are no longer shipped in scripts/.
Use the unified nostream CLI directly instead:
scripts/start -> nostream start
scripts/start_with_tor -> nostream start --tor
scripts/start_with_i2p -> nostream start --i2p
scripts/start_with_nginx -> nostream start --nginx
scripts/stop -> nostream stop
scripts/print_tor_hostname -> nostream info --tor-hostname
scripts/print_i2p_hostname -> nostream info --i2p-hostname
scripts/update -> nostream update
scripts/clean -> nostream clean
You can import NIP-01 events from .jsonl (JSON Lines) or .json (JSON array) files directly into the relay database.
Compressed .jsonl files are also supported and decompressed on-the-fly:
.jsonl.gz (Gzip).jsonl.xz (XZ)Basic import:
nostream import ./events.jsonl
Equivalent alias form:
nostream import --file ./events.jsonl
Import from a JSON array file (compatible with nostream export --format json):
nostream import --file ./events.json
Import a compressed backup:
nostream import ./events.jsonl.gz
nostream import ./events.jsonl.xz
Set a custom batch size (default: 1000):
nostream import ./events.jsonl --batch-size 500
The importer:
[Processed: 50,000 | Inserted: 45,000 | Skipped: 5,000 | Errors: 0]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=/usr/bin/env bash -lc 'cd /home/nostr/nostream && nostream start' ExecStop=/usr/bin/env bash -lc 'cd /home/nostr/nostream && nostream stop'
[Install] WantedBy=multi-user.target ```
And then:
systemctl enable nostream
systemctl start nostream
The logs can be viewed with:
journalctl -u nostream
EAI_AGAIN)On some Linux environments (especially rolling-release distros or setups using
systemd-resolved), docker compose builds can fail with DNS errors such as:
getaddrinfo EAI_AGAIN registry.npmjs.org$ claude mcp add nostream \
-- python -m otcore.mcp_server <graph>