MCPcopy Index your code
hub / github.com/cameri/nostream

github.com/cameri/nostream @v3.0.0

Chat with this repo
repository ↗ · DeepWiki ↗ · release v3.0.0 ↗ · + Follow
1,119 symbols 3,264 edges 332 files 13 documented · 1% updated 3d agov3.0.0 · 2026-06-21★ 81564 open issues
What it actually does AI analysis from the code graph — generated when you open this
loading…
README

nostream

nostream logo

GitHub release GitHub issues GitHub stars GitHub top language GitHub forks GitHub license Coverage Status 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.

  • [x] NIP-01: Basic protocol flow description
  • [x] NIP-02: Contact list and petnames
  • [x] NIP-03: OpenTimestamps Attestations for Events
  • [x] NIP-04: Encrypted Direct Message
  • [x] NIP-05: Mapping Nostr keys to DNS-based internet identifiers
  • [x] NIP-09: Event deletion
  • [x] NIP-11: Relay information document
  • [x] NIP-12: Generic tag queries
  • [x] NIP-13: Proof of Work
  • [x] NIP-14: Subject tag in text events
  • [x] NIP-15: End of Stored Events Notice
  • [x] NIP-16: Event Treatment
  • [x] NIP-20: Command Results
  • [x] NIP-22: Event created_at Limits
  • [x] NIP-25: Reactions
  • [ ] NIP-26: Delegated Event Signing (REMOVED)
  • [x] NIP-28: Public Chat
  • [x] NIP-33: Parameterized Replaceable Events
  • [x] NIP-40: Expiration Timestamp
  • [x] NIP-44: Encrypted Payloads (Versioned)
  • [x] NIP-45: Event Counts
  • [x] NIP-62: Request to Vanish
  • [x] NIP-65: Relay List Metadata

Requirements

Standalone setup

  • PostgreSQL 14.0
  • Redis
  • Node v24
  • 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

  1. Before you begin
  2. Complete one of the Quick Start guides in this document
  3. Create a .env file
  4. 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)
  5. Choose one of the following payment processors: zebedee, nodeless, opennode, lnbits, lnurl, nwc

  6. ZEBEDEE

  7. Complete the step "Before you begin"
  8. Sign up for a ZEBEDEE Developer Dashboard account, create a new LIVE Project, and get that Project's API Key
  9. Set ZEBEDEE_API_KEY environment variable with the API Key above on your .env file

    ZEBEDEE_API_KEY={YOUR_ZEBEDEE_API_KEY_HERE}

  10. Follow the required steps for all payments processors

  11. 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)
  12. Restart Nostream (nostream stop followed by nostream start)
  13. Read the in-depth guide for more information: Set Up a Paid Nostr Relay with ZEBEDEE API

  14. Nodeless

  15. Complete the step "Before you begin"
  16. Sign up for a new account, create a new store and take note of the store ID
  17. Go to Profile > API Tokens and generate a new key and take note of it
  18. 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
  19. 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}

  20. On your .nostr/settings.yaml file make the following changes:

    • Set payments.processor to nodeless
    • Set paymentsProcessors.nodeless.storeId to your store ID
  21. Restart Nostream (nostream stop followed by nostream start)

  22. OpenNode

  23. Complete the step "Before you begin"
  24. Sign up for a new account and get verified
  25. Go to Developers > Integrations and setup two-factor authentication
  26. Create a new API Key with Invoices permission
  27. Set OPENNODE_API_KEY environment variable on your .env file

    OPENNODE_API_KEY={YOUR_OPENNODE_API_KEY}

  28. On your .nostr/settings.yaml file make the following changes:

    • Set payments.processor to opennode
  29. Restart Nostream (nostream stop followed by nostream start)

  30. 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 (nostream stop followed by nostream start)

  31. 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 (nostream stop followed by nostream start)
  32. 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)
  33. Ensure payments are required for your public key

  34. Visit https://{YOUR-DOMAIN}/
  35. You should be presented with a form requesting an admission fee to be paid
  36. Fill out the form and take the necessary steps to pay the invoice
  37. Wait until the screen indicates that payment was received
  38. Add your relay URL to your favorite Nostr client (wss://{YOUR-DOMAIN}) and wait for it to connect
  39. Send a couple notes to test
  40. Go to https://websocketking.com/ and connect to your relay (wss://{YOUR_DOMAIN})
  41. Convert your npub to hexadecimal using a Key Converter
  42. Send the following JSON message: ["REQ", "payment-test", {"authors":["your-pubkey-in-hexadecimal"]}]
  43. 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. 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

Importing events from JSON Lines or JSON Arrays

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:

  • Processes the file line-by-line to keep memory usage bounded.
  • Streams JSON array items one by one to keep memory usage bounded.
  • Validates NIP-01 schema, event id hash, and Schnorr signature before insertion.
  • Inserts in database transactions per batch.
  • Skips duplicates without failing the whole import.
  • Prints progress in the format: [Processed: 50,000 | Inserted: 45,000 | Skipped: 5,000 | Errors: 0]

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=/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

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
  • `Tempor

Extension points exported contracts — how you extend this code

IRunnable (Interface)
(no doc) [8 implementers]
src/@types/base.ts
NostrEvent (Interface)
Minimal Nostr event shape for NIP-03 smoke (kind 1040).
scripts/smoke-nip03.ts
NostrEvent (Interface)
A valid serialised Nostr event (NIP-01).
scripts/security-load-test.ts
CliOptions (Interface)
(no doc)
src/import-events.ts
OtsAttestation (Interface)
(no doc)
src/utils/nip03.ts
EventImportStats (Interface)
(no doc)
src/services/event-import-service.ts
IController (Interface)
(no doc) [16 implementers]
src/@types/controllers.ts
OtsClientResult (Interface)
(no doc)
scripts/smoke-nip03.ts

Core symbols most depended-on inside this repo

toString
called by 178
src/handlers/event-message-handler.ts
validateSchema
called by 77
src/utils/validation.ts
handleRequest
called by 67
src/@types/controllers.ts
createLogger
called by 66
src/factories/logger-factory.ts
execute
called by 59
src/@types/message-handlers.ts
canAcceptEvent
called by 48
src/app/static-mirroring-worker.ts
createEvent
called by 44
test/integration/features/helpers.ts
logInfo
called by 40
src/cli/utils/output.ts

Shape

Function 554
Method 337
Class 112
Interface 105
Enum 11

Languages

TypeScript100%

Modules by API surface

src/@types/settings.ts43 symbols
src/@types/repositories.ts33 symbols
src/utils/event.ts30 symbols
src/repositories/event-repository.ts27 symbols
src/adapters/web-socket-adapter.ts21 symbols
src/utils/nip03.ts20 symbols
scripts/smoke-nip03.ts19 symbols
src/handlers/event-message-handler.ts18 symbols
src/cli/utils/config.ts18 symbols
src/adapters/redis-adapter.ts18 symbols
src/@types/adapters.ts18 symbols
src/services/event-import-service.ts17 symbols

Datastores touched

nostr_ts_relay_testDatabase · 1 repos

For agents

$ claude mcp add nostream \
  -- python -m otcore.mcp_server <graph>

⬇ download graph artifact

Ask about this repo answers extend the page