From 7e3b3f4a27322c2762eb9926a52805422eedf4df Mon Sep 17 00:00:00 2001
From: chappie-daemon
Date: Fri, 16 Oct 2026 19:16:05 -0400
Subject: [PATCH 1/5] docs: link the project website from the README intro
---
README.md | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/README.md b/README.md
index 011631dba..abd8e4e50 100644
--- a/README.md
+++ b/README.md
@@ -33,7 +33,8 @@
This is a [nostr](https://github.com/fiatjaf/nostr) relay, written in
-Typescript.
+Typescript. Documentation, features and guides live on the project website:
+**[ema.tnsor.network](https://ema.tnsor.network)**.
This implementation is production-ready. See below for supported features.
From 4818324bcc4bd47c28fc2700267dd606f8cc10f1 Mon Sep 17 00:00:00 2001
From: chappie-daemon
Date: Fri, 16 Oct 2026 19:19:27 -0400
Subject: [PATCH 2/5] docs: add the changeset the repo requires
---
.changeset/readme-website-link.md | 5 +++++
1 file changed, 5 insertions(+)
create mode 100644 .changeset/readme-website-link.md
diff --git a/.changeset/readme-website-link.md b/.changeset/readme-website-link.md
new file mode 100644
index 000000000..36962d3c4
--- /dev/null
+++ b/.changeset/readme-website-link.md
@@ -0,0 +1,5 @@
+---
+"nostream-website": patch
+---
+
+README now links the project website (ema.tnsor.network) from the intro.
From 79fdf014b489458cb6f43ec1e87c13fc464ff8b2 Mon Sep 17 00:00:00 2001
From: chappie-daemon
Date: Fri, 16 Oct 2026 19:29:58 -0400
Subject: [PATCH 3/5] docs: modernize the README
Add a table of contents, fix the NIP checklist to match
package.json's supportedNips (adds NIP-42/43/50/77, marks NIP-26 as
removed upstream, drops the duplicate NIP-44), reorder the major
sections into a start-first flow (quick starts before the payments
guide, standalone next to Docker, troubleshooting near the end), fix
the stale ./scripts reference in the NWC step (the wrapper scripts
are gone), and normalize the indented code fences to plain ones.
No factual content changed: URLs, environment variable names and
command invocations are byte-identical to the previous version.
---
README.md | 675 ++++++++++++++++++++++++++++--------------------------
1 file changed, 350 insertions(+), 325 deletions(-)
diff --git a/README.md b/README.md
index abd8e4e50..6b9965e5f 100644
--- a/README.md
+++ b/README.md
@@ -42,6 +42,26 @@ The project master repository is available on [GitHub](https://github.com/cameri
[](https://railway.app/new/template/Xfk5F7?referralCode=Kfv2ly)
+## Table of contents
+
+- [Features](#features)
+- [Requirements](#requirements)
+- [Quick start (Docker Compose)](#quick-start-docker-compose)
+ - [Importing events from JSON Lines or JSON Arrays](#importing-events-from-json-lines-or-json-arrays)
+ - [NIP-43 invite codes](#nip-43-invite-codes)
+- [Quick start (Standalone)](#quick-start-standalone)
+- [Full guide](#full-guide)
+ - [Accepting payments](#accepting-payments)
+- [Running as a Service](#running-as-a-service)
+- [Troubleshooting](#troubleshooting)
+- [Export Events](#export-events)
+- [Benchmark Database Queries](#benchmark-database-queries)
+- [Relay Maintenance](#relay-maintenance)
+- [Configuration](#configuration)
+- [Development & Contributing](#development--contributing)
+- [Dev Channel](#dev-channel)
+- [License](#license)
+
## Features
NIPs and MIPs with a relay-specific implementation are listed here.
@@ -61,14 +81,19 @@ NIPs and MIPs with a relay-specific implementation are listed here.
- [x] NIP-20: Command Results
- [x] NIP-22: Event `created_at` Limits
- [x] NIP-25: Reactions
-- [ ] NIP-26: Delegated Event Signing (REMOVED)
+- [ ] NIP-26: Delegated Event Signing (removed upstream; not implemented)
- [x] NIP-28: Public Chat
- [x] NIP-33: Parameterized Replaceable Events
- [x] NIP-40: Expiration Timestamp
+- [x] NIP-42: Authentication of clients to relays
+- [x] NIP-43: Invite codes
- [x] NIP-44: Encrypted Payloads (Versioned)
- [x] NIP-45: Event Counts
+- [x] NIP-50: Search capability
- [x] NIP-62: Request to Vanish
- [x] NIP-65: Relay List Metadata
+- [x] NIP-77: Negentropy Synchronization
+
- [x] Marmot Protocol [MIPs 00–03](https://github.com/marmot-protocol/marmot): E2EE group messaging over Nostr — kinds 443, 444, 445, 10051 and 30443, with `supported_mips` advertised in the NIP-11 relay information document
## Requirements
@@ -90,120 +115,6 @@ NIPs and MIPs with a relay-specific implementation are listed here.
WARNING: Docker distributions from Snap, Brew or Debian repositories are NOT SUPPORTED and will result in errors.
Install Docker from their [official guide](https://docs.docker.com/engine/install/) ONLY.
-## Full Guide
-
-- [Set up a Paid Nostr relay with Nostream and ZEBEDEE](https://docs.zbdpay.com/payments/nostr/nostr-relay) by [André Neves](https://primal.net/andre) (CTO & Co-Founder at [ZEBEDEE](https://zebedee.io/))
-- [Set up a Nostr relay in under 5 minutes](https://andreneves.xyz/p/set-up-a-nostr-relay-server-in-under) by [André Neves](https://twitter.com/andreneves) (CTO & Co-Founder at [ZEBEDEE](https://zebedee.io/))
-
-### Accepting Payments
-
-1. 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`, `nwc`
-
-2. [ZEBEDEE](https://zebedee.io)
- - Complete the step "Before you begin"
- - [Sign up for a ZEBEDEE Developer Dashboard account](https://dashboard.zebedee.io/signup), create a new LIVE Project, and get that Project's API Key
- - 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
- - 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 (`nostream stop` followed by `nostream start`)
- - Read the in-depth guide for more information: [Set Up a Paid Nostr Relay with ZEBEDEE API](https://docs.zbdpay.com/payments/nostr/nostr-relay)
-
-3. [Nodeless](https://nodeless.io/?ref=587f477f-ba1c-4bd3-8986-8302c98f6731)
- - Complete the step "Before you begin"
- - [Sign up](https://nodeless.io/?ref=587f477f-ba1c-4bd3-8986-8302c98f6731) 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 (`nostream stop` followed by `nostream start`)
-
-4. [OpenNode](https://www.opennode.com/)
- - 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 (`nostream stop` followed by `nostream start`)
-
-5. [LNBITS](https://lnbits.com/)
- - Complete the step "Before you begin"
- - Create a new wallet on you public LNbits instance
- - [Demo](https://legend.lnbits.com/) 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 (`nostream stop` followed by `nostream start`)
-
-6. [Alby](https://getalby.com/) or any LNURL Provider with [LNURL-verify](https://github.com/lnurl/luds/issues/182) support
- - Complete the step "Before you begin"
- - [Create a new account](https://getalby.com/user/new) 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 (`nostream stop` followed by `nostream start`)
-
-7. Nostr Wallet Connect (NIP-47 / NWC)
- - Complete the step "Before you begin"
- - Create an app connection in your NWC-compatible wallet and copy the generated NWC URL
- - Set `NWC_URL` environment variable on your `.env` file
-
- ```
- NWC_URL={NOSTR_WALLET_CONNECT_URL}
- ```
-
- - On your `.nostr/settings.yaml` file make the following changes:
- - Set `payments.processor` to `nwc`
- - Restart Nostream (`./scripts/stop` followed by `./scripts/start`)
-
-8. 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](https://damus.io/key/)
- - 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)
For full command reference and interactive mode documentation, see [CLI.md](CLI.md).
@@ -213,59 +124,59 @@ Non-interactive CLI usage conventions:
- exit `2` on usage errors (invalid command/options)
Optional global installation from a source checkout:
- ```
- pnpm add -g .
- nostream --help
- ```
+```
+pnpm add -g .
+nostream --help
+```
Install Docker following the [official guide](https://docs.docker.com/engine/install/).
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
- ```
+```
+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
- ```
+```
+SECRET=aaabbbccc...dddeeefff
+# Secret shortened for brevity
+```
Start:
- ```
- nostream start
- ```
+```
+nostream start
+```
or
- ```
- nostream start --tor
- ```
+```
+nostream start --tor
+```
or
- ```
- nostream start --i2p
- ```
+```
+nostream start --i2p
+```
or
- ```
- RELAY_DOMAIN=relay.example.com CERTBOT_EMAIL=you@example.com nostream start --nginx
- ```
+```
+RELAY_DOMAIN=relay.example.com CERTBOT_EMAIL=you@example.com nostream start --nginx
+```
Stop the server with:
- ```
- nostream stop
- ```
+```
+nostream stop
+```
Print the Tor hostname:
- ```
- nostream info --tor-hostname
- ```
+```
+nostream info --tor-hostname
+```
Print I2P hostname(s):
- ```
- nostream info --i2p-hostname
- ```
+```
+nostream info --i2p-hostname
+```
The old shell wrapper scripts are no longer shipped in `scripts/`.
Use the unified `nostream` CLI directly instead:
@@ -292,30 +203,30 @@ Compressed `.jsonl` files are also supported and decompressed on-the-fly:
- `.jsonl.xz` (XZ)
Basic import:
- ```
- nostream import ./events.jsonl
- ```
+```
+nostream import ./events.jsonl
+```
Equivalent alias form:
- ```
- nostream import --file ./events.jsonl
- ```
+```
+nostream import --file ./events.jsonl
+```
Import from a JSON array file (compatible with `nostream export --format json`):
- ```
- nostream import --file ./events.json
- ```
+```
+nostream import --file ./events.json
+```
Import a compressed backup:
- ```
- nostream import ./events.jsonl.gz
- nostream import ./events.jsonl.xz
- ```
+```
+nostream import ./events.jsonl.gz
+nostream import ./events.jsonl.xz
+```
Set a custom batch size (default: `1000`):
- ```
- nostream import ./events.jsonl --batch-size 500
- ```
+```
+nostream import ./events.jsonl --batch-size 500
+```
The importer:
@@ -331,10 +242,10 @@ The importer:
Kind 28934 join requests are implemented. Mint a code and share it out of band:
- ```
- nostream invite create --json
- docker compose exec nostream node src/cli/index.js invite create
- ```
+```
+nostream invite create --json
+docker compose exec nostream node src/cli/index.js invite create
+```
Users can also request a code themselves: the relay answers a `REQ` for kind 28935 by
minting one on the fly. Off by default — set `nip43.allowInviteRequests` to enable it.
@@ -342,199 +253,259 @@ minting one on the fly. Off by default — set `nip43.allowInviteRequests` to en
See [CLI.md](CLI.md) for `--uses` / `--expires-in` and Docker vs local Postgres, and
[CONFIGURATION.md](CONFIGURATION.md) for the invite request settings.
-### Running as a Service
+## Quick Start (Standalone)
-By default this server will run continuously until you stop it with Ctrl+C or until the system restarts.
+Set the following environment variables:
-You can [install as a systemd service](https://www.swissrouting.com/nostr.html#installing-as-a-service) if you want the server to run again automatically whenever the system is restarted. For example:
+```
+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
+```
- ```
- $ nano /etc/systemd/system/nostream.service
+```
+REDIS_URI="redis://default:nostr_ts_relay@localhost:6379"
- # Note: replace "User=..." with your username, and
- # "/home/nostr/nostream" with the directory where you cloned the repo.
+REDIS_HOST=localhost
+REDIS_PORT=6379
+REDIS_USER=default
+REDIS_PASSWORD=nostr_ts_relay
+```
- [Unit]
- Description=Nostr TS Relay
- After=network.target
- StartLimitIntervalSec=0
+Generate a long random secret and set SECRET:
+You may want to use `openssl rand -hex 128` to generate a secret.
- [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'
+```
+SECRET=aaabbbccc...dddeeefff
+# Secret shortened for brevity
+```
- [Install]
- WantedBy=multi-user.target
- ```
+### Initializing the database
-And then:
+Create `nostr_ts_relay` database:
- ```
- systemctl enable nostream
- systemctl start nostream
- ```
+```
+$ psql -h $DB_HOST -p $DB_PORT -U $DB_USER -W
+postgres=# create database nostr_ts_relay;
+postgres=# quit
+```
-The logs can be viewed with:
+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
+```
- ```
- journalctl -u nostream
- ```
+Clone repository and enter directory:
+```
+git clone git@github.com:Cameri/nostream.git
+cd nostream
+```
-## Troubleshooting
+Install dependencies:
-### Linux: Docker DNS resolution failures (`EAI_AGAIN`)
+```
+corepack enable
+pnpm install
+```
-On some Linux environments (especially rolling-release distros or setups using
-`systemd-resolved`), `docker compose` builds can fail with DNS errors such as:
+Run migrations (at least once and after pulling new changes):
-- `getaddrinfo EAI_AGAIN registry.npmjs.org`
-- `Temporary failure in name resolution`
+```
+pnpm db:migrate
+```
-To fix this, configure Docker daemon DNS in `/etc/docker/daemon.json`.
+Create .nostr folder inside nostream project folder and copy over the settings file:
-1. Create or update `/etc/docker/daemon.json`:
+```
+mkdir .nostr
+cp resources/default-settings.yaml .nostr/settings.yaml
+```
- ```
- sudo mkdir -p /etc/docker
- sudo nano /etc/docker/daemon.json
- ```
+To start in development mode:
- Add or update the file with:
+```
+pnpm dev
+```
- ```
- {
- "dns": ["1.1.1.1", "8.8.8.8"]
- }
- ```
+Or, start in production mode:
- If this file already exists, merge the `dns` key into the existing JSON
- instead of replacing the entire file.
+```
+pnpm start
+```
- If your environment does not allow public resolvers, replace `1.1.1.1` and
- `8.8.8.8` with DNS servers approved by your network.
+To clean up the build, coverage and test reports run:
-2. Restart Docker:
+```
+pnpm clean
+```
+## Full Guide
- ```
- sudo systemctl restart docker
- ```
+- [Set up a Paid Nostr relay with Nostream and ZEBEDEE](https://docs.zbdpay.com/payments/nostr/nostr-relay) by [André Neves](https://primal.net/andre) (CTO & Co-Founder at [ZEBEDEE](https://zebedee.io/))
+- [Set up a Nostr relay in under 5 minutes](https://andreneves.xyz/p/set-up-a-nostr-relay-server-in-under) by [André Neves](https://twitter.com/andreneves) (CTO & Co-Founder at [ZEBEDEE](https://zebedee.io/))
-3. Verify DNS works inside containers:
+### Accepting Payments
- ```
- docker run --rm busybox nslookup registry.npmjs.org
- ```
+1. 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`, `nwc`
-4. Retry starting nostream:
+2. [ZEBEDEE](https://zebedee.io)
+ - Complete the step "Before you begin"
+ - [Sign up for a ZEBEDEE Developer Dashboard account](https://dashboard.zebedee.io/signup), create a new LIVE Project, and get that Project's API Key
+ - Set `ZEBEDEE_API_KEY` environment variable with the API Key above on your `.env` file
- ```
- nostream start
- ```
+```
+ ZEBEDEE_API_KEY={YOUR_ZEBEDEE_API_KEY_HERE}
+```
-Note: avoid `127.0.0.53` in Docker DNS settings because it points to the host's
-local resolver stub and is often unreachable from containers.
+ - 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 (`nostream stop` followed by `nostream start`)
+ - Read the in-depth guide for more information: [Set Up a Paid Nostr Relay with ZEBEDEE API](https://docs.zbdpay.com/payments/nostr/nostr-relay)
-## Quick Start (Standalone)
+3. [Nodeless](https://nodeless.io/?ref=587f477f-ba1c-4bd3-8986-8302c98f6731)
+ - Complete the step "Before you begin"
+ - [Sign up](https://nodeless.io/?ref=587f477f-ba1c-4bd3-8986-8302c98f6731) 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
-Set the following environment variables:
+```
+ NODELESS_API_KEY={YOUR_NODELESS_API_KEY}
+ NODELESS_WEBHOOK_SECRET={YOUR_NODELESS_WEBHOOK_SECRET}
+```
- ```
- 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
- ```
+ - 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 (`nostream stop` followed by `nostream start`)
-Generate a long random secret and set SECRET:
-You may want to use `openssl rand -hex 128` to generate a secret.
+4. [OpenNode](https://www.opennode.com/)
+ - 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
- ```
- SECRET=aaabbbccc...dddeeefff
- # Secret shortened for brevity
- ```
+ ```
+ OPENNODE_API_KEY={YOUR_OPENNODE_API_KEY}
+ ```
-### Initializing the database
+ - On your `.nostr/settings.yaml` file make the following changes:
+ - Set `payments.processor` to `opennode`
+ - Restart Nostream (`nostream stop` followed by `nostream start`)
-Create `nostr_ts_relay` database:
+5. [LNBITS](https://lnbits.com/)
+ - Complete the step "Before you begin"
+ - Create a new wallet on you public LNbits instance
+ - [Demo](https://legend.lnbits.com/) 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
- ```
- $ psql -h $DB_HOST -p $DB_PORT -U $DB_USER -W
- postgres=# create database nostr_ts_relay;
- postgres=# quit
- ```
+ ```
+ 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 (`nostream stop` followed by `nostream start`)
-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
- ```
+6. [Alby](https://getalby.com/) or any LNURL Provider with [LNURL-verify](https://github.com/lnurl/luds/issues/182) support
+ - Complete the step "Before you begin"
+ - [Create a new account](https://getalby.com/user/new) 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 (`nostream stop` followed by `nostream start`)
-Clone repository and enter directory:
- ```
- git clone git@github.com:Cameri/nostream.git
- cd nostream
- ```
+7. Nostr Wallet Connect (NIP-47 / NWC)
+ - Complete the step "Before you begin"
+ - Create an app connection in your NWC-compatible wallet and copy the generated NWC URL
+ - Set `NWC_URL` environment variable on your `.env` file
-Install dependencies:
+ ```
+ NWC_URL={NOSTR_WALLET_CONNECT_URL}
+ ```
- ```
- corepack enable
- pnpm install
- ```
+ - On your `.nostr/settings.yaml` file make the following changes:
+ - Set `payments.processor` to `nwc`
+ - Restart Nostream (`nostream stop` followed by `nostream start`)
-Run migrations (at least once and after pulling new changes):
+8. 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](https://damus.io/key/)
+ - Send the following JSON message: `["REQ", "payment-test", {"authors":["your-pubkey-in-hexadecimal"]}]`
+ - You should get back the few notes you sent earlier
- ```
- pnpm db:migrate
- ```
+## Running as a Service
-Create .nostr folder inside nostream project folder and copy over the settings file:
+By default this server will run continuously until you stop it with Ctrl+C or until the system restarts.
- ```
- mkdir .nostr
- cp resources/default-settings.yaml .nostr/settings.yaml
- ```
+You can [install as a systemd service](https://www.swissrouting.com/nostr.html#installing-as-a-service) if you want the server to run again automatically whenever the system is restarted. For example:
-To start in development mode:
+```
+$ 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
+```
- ```
- pnpm dev
- ```
+And then:
-Or, start in production mode:
+```
+systemctl enable nostream
+systemctl start nostream
+```
- ```
- pnpm start
- ```
+The logs can be viewed with:
-To clean up the build, coverage and test reports run:
+```
+journalctl -u nostream
+```
- ```
- pnpm clean
- ```
## Development & Contributing
For development environment setup, testing, linting, load testing, and contribution guidelines
@@ -606,38 +577,92 @@ corresponding data from the derived `event_tags` table when present.
Dry run (no deletion):
- ```
- nostream dev db:clean --all --dry-run
- ```
+```
+nostream dev db:clean --all --dry-run
+```
Full wipe:
- ```
- nostream dev db:clean --all --force
- ```
+```
+nostream dev db:clean --all --force
+```
Delete events older than N days:
- ```
- nostream dev db:clean --older-than=30 --force
- ```
+```
+nostream dev db:clean --older-than=30 --force
+```
Delete only selected kinds:
- ```
- nostream dev db:clean --kinds=1,7,4 --force
- ```
+```
+nostream dev db:clean --kinds=1,7,4 --force
+```
Delete only selected kinds older than N days:
- ```
- nostream dev db:clean --older-than=30 --kinds=1,7,4 --force
- ```
+```
+nostream dev db:clean --older-than=30 --kinds=1,7,4 --force
+```
By default, the command asks for explicit confirmation (`Type 'DELETE' to confirm`).
Use `--force` to skip the prompt.
+## Troubleshooting
+
+### Linux: Docker DNS resolution failures (`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`
+- `Temporary failure in name resolution`
+
+To fix this, configure Docker daemon DNS in `/etc/docker/daemon.json`.
+
+1. Create or update `/etc/docker/daemon.json`:
+
+```
+sudo mkdir -p /etc/docker
+sudo nano /etc/docker/daemon.json
+```
+
+ Add or update the file with:
+
+```
+{
+ "dns": ["1.1.1.1", "8.8.8.8"]
+}
+```
+
+ If this file already exists, merge the `dns` key into the existing JSON
+ instead of replacing the entire file.
+
+ If your environment does not allow public resolvers, replace `1.1.1.1` and
+ `8.8.8.8` with DNS servers approved by your network.
+
+2. Restart Docker:
+
+```
+sudo systemctl restart docker
+```
+
+3. Verify DNS works inside containers:
+
+```
+docker run --rm busybox nslookup registry.npmjs.org
+```
+
+4. Retry starting nostream:
+
+```
+nostream start
+```
+
+Note: avoid `127.0.0.53` in Docker DNS settings because it points to the host's
+local resolver stub and is often unreachable from containers.
+
## Configuration
You can change the default folder by setting the `NOSTR_CONFIG_DIR` environment variable to a different path.
From dd55552cd28dbc985518f26900f997c2a131a6c3 Mon Sep 17 00:00:00 2001
From: chappie-daemon
Date: Fri, 16 Oct 2026 19:36:29 -0400
Subject: [PATCH 4/5] =?UTF-8?q?docs:=20drop=20the=20changeset=20=E2=80=94?=
=?UTF-8?q?=20a=20README=20change=20does=20not=20release=20the=20website?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.changeset/readme-website-link.md | 5 -----
1 file changed, 5 deletions(-)
delete mode 100644 .changeset/readme-website-link.md
diff --git a/.changeset/readme-website-link.md b/.changeset/readme-website-link.md
deleted file mode 100644
index 36962d3c4..000000000
--- a/.changeset/readme-website-link.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-"nostream-website": patch
----
-
-README now links the project website (ema.tnsor.network) from the intro.
From 286dab5a96b8ff979d5f83bac3584d88cbba1b30 Mon Sep 17 00:00:00 2001
From: chappie-daemon
Date: Fri, 16 Oct 2026 19:43:22 -0400
Subject: [PATCH 5/5] =?UTF-8?q?docs:=20add=20an=20empty=20changeset=20?=
=?UTF-8?q?=E2=80=94=20docs-only=20change,=20no=20release?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.changeset/ten-radios-sniff.md | 2 ++
1 file changed, 2 insertions(+)
create mode 100644 .changeset/ten-radios-sniff.md
diff --git a/.changeset/ten-radios-sniff.md b/.changeset/ten-radios-sniff.md
new file mode 100644
index 000000000..a845151cc
--- /dev/null
+++ b/.changeset/ten-radios-sniff.md
@@ -0,0 +1,2 @@
+---
+---