When contributing to this repository, please first discuss the change you wish to make via issue, email, or any other method with the owners of this repository before making a change.
Please keep the conversations civil, respectful and focus on the topic being discussed.
To keep the project moving fairly for everyone, please follow these guidelines:
- Search before submitting. Before opening a new issue or PR, search existing issues to avoid duplicates or overlapping work.
- All PRs must have an associated issue. Open or find a relevant issue before starting work. Starting a PR without a linked issue means your work may not be merged, and it does not grant automatic assignment of an issue.
- Avoid snowball PRs. Keep pull requests focused. Do not mix unrelated fixes, features, or changes in a single PR.
- Prefer older issues first. When choosing what to work on, prefer resolving older open issues before newer ones, unless a blocker exists or the maintainers have explicitly agreed otherwise.
- Abandoned assignments. An issue assigned to a contributor and not worked on for 7 days (no comments or commits) is considered abandoned, unless the assignee is actively working on it or has requested an extension from a maintainer.
- Extensions and transfers. Assignments can be explicitly extended or transferred to another contributor if the original assignee is unresponsive.
- Release your assignment. If you are no longer interested in an issue, please comment to request being unassigned so others can pick it up.
Install Docker Desktop following the official guide (if you plan to use Docker). You may have to uninstall Docker on your machine if you installed it using a different guide.
Clone the repository and enter the directory:
git clone git@github.com:Cameri/nostream.git
cd nostream
Install dependencies (this also sets up Husky pre-commit hooks automatically):
npm install
Important: Pre-commit hooks installed by Husky run linting and formatting checks on every commit. Do not bypass them with
git commit --no-verify. If a hook fails, fix the reported issues before committing.
Start the relay (runs in the foreground until stopped with Ctrl+C):
./scripts/start
Set the required environment variables (or copy .env.example to .env and edit it):
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:
SECRET=aaabbbccc...dddeeefff
# Secret shortened for brevity
Run migrations (at least once and after pulling new changes):
NODE_OPTIONS="-r dotenv/config" npm run db:migrate
Create the .nostr folder and copy the default settings file:
mkdir .nostr
cp resources/default-settings.yaml .nostr/settings.yaml
Start in development mode:
npm run dev
Or start in production mode:
npm run start
To clean up build, coverage, and test reports:
npm run clean
Run code quality checks with Biome:
npm run lint
npm run lint:fix
npm run format
npm run format:check
Change to the project's directory:
cd /path/to/nostream
Run unit tests:
npm run test:unit
Run unit tests in watch mode:
npm run test:unit:watch
Get unit test coverage:
npm run cover:unit
Open the unit test report:
open .test-reports/unit/index.html
Open the unit test coverage report:
open .coverage/unit/lcov-report/index.html
Change to the project's directory:
cd /path/to/nostream
Run integration tests:
npm run docker:test:integration
Get integration test coverage:
npm run docker:cover:integration
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
Run the integration tests:
npm run test:integration
Open the integration test report:
open .test-reports/integration/report.html
Get integration test coverage:
npm run cover:integration
Open the integration test coverage report:
open .coverage/integration/lcov-report/index.html
Nostream includes a specialized security tester to simulate Slowloris-style connection holding and event flood (spam) attacks. This is used to verify relay resilience and prevent memory leaks.
# Simulates 5,000 idle "zombie" connections + 100 events/sec spam
npm run test:load -- --zombies 5000 --spam-rate 100To verify that connections are being correctly evicted and memory reclaimed:
- Ensure the relay is running with
--inspectenabled (seedocker-compose.yml). - Open Chrome DevTools (
chrome://inspect) and connect to the relay process. - In the Memory tab, take a Heap Snapshot (Baseline).
- Run the load tester.
- Wait for the eviction cycle (default: 120s) and take a second Heap Snapshot.
- Switch the view to Comparison and select the Baseline snapshot.
- Verify that object counts (e.g.,
WebSocketAdapter,SocketAddress) return to baseline levels.
To observe client and subscription counts in real-time during a test, you can instrument
src/adapters/web-socket-server-adapter.ts:
- Locate the
onHeartbeat()method. - Add the following logging logic:
private onHeartbeat() { let totalSubs = 0; let totalClients = 0; this.webSocketServer.clients.forEach((webSocket) => { totalClients++; const webSocketAdapter = this.webSocketsAdapters.get(webSocket) as IWebSocketAdapter; if (webSocketAdapter) { webSocketAdapter.emit(WebSocketAdapterEvent.Heartbeat); totalSubs += webSocketAdapter.getSubscriptions().size; } }); console.log(`[HEARTBEAT] Clients: ${totalClients} | Total subscriptions: ${totalSubs} | Heap Used: ${(process.memoryUsage().heapUsed / 1024 / 1024).toFixed(1)} MB`); }
- View the live output via Docker logs:
docker compose logs -f nostream
Run dead code and dependency analysis before opening a pull request:
npm run knip
npm run lint now runs Biome.
- Update the relevant documentation with details of changes to the interface, this includes new environment variables, exposed ports, useful file locations and container parameters.
- Follow the versioning and changeset process described in Releases & Versioning.
- You may merge the Pull Request in once you have the sign-off of two other developers, or if you do not have permission to do that, you may request the second reviewer to merge it for you.
This project uses Changesets for version management.
Every pull request that changes behavior, adds a feature, or fixes a bug must include a changeset file. The CI changeset-check job will fail if no changeset is present.
To add a changeset:
npx changesetThis interactive prompt will ask you to:
- Select the bump type:
major,minor, orpatch - Write a short summary of the change (this becomes the changelog entry)
The command creates a file in .changeset/ — commit it with your PR.
If your PR only updates documentation, CI/CD configuration, or test coverage — and leaves all production source code untouched — an empty changeset is acceptable:
npx changeset --emptyCommit the generated .changeset/*.md file with your PR. This satisfies CI without producing a
version bump or changelog entry.
This applies to PRs that exclusively contain:
- Documentation updates (README, CONTRIBUTING, CONFIGURATION, etc.)
- CI/CD workflow changes (
.github/files) - Test additions or improvements (when no source code is changed)
- Changesets accumulate as PRs are merged to
main - The
Changesets Releaseworkflow automatically opens a "chore: release new version 🚀" PR that aggregates all pending changesets, bumpspackage.json, and updatesCHANGELOG.md - When a maintainer merges the "chore: release new version 🚀" PR, the workflow publishes a GitHub release and creates the corresponding git tag
- The Docker image is then automatically built and pushed to GHCR via the
release.ymlworkflow
Run Biome checks before opening a pull request:
npm run lint
npm run format:check