Skip to content

Latest commit

 

History

History
367 lines (259 loc) · 9.25 KB

File metadata and controls

367 lines (259 loc) · 9.25 KB

Contributing

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.

Issue Assignment & Fairness Policy

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.

Development Environment Setup

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.

Development Quick Start (Docker Compose)

Start the relay (runs in the foreground until stopped with Ctrl+C):

./scripts/start

Development Quick Start (Standalone)

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

Tests

Linting and formatting (Biome)

Run code quality checks with Biome:

npm run lint
npm run lint:fix
npm run format
npm run format:check

Unit tests

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

Integration tests (Docker Compose)

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

Integration tests (Standalone)

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

Security & Load Testing

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.

Running the Tester

# Simulates 5,000 idle "zombie" connections + 100 events/sec spam
npm run test:load -- --zombies 5000 --spam-rate 100

Analyzing Memory (Heap Snapshots)

To verify that connections are being correctly evicted and memory reclaimed:

  1. Ensure the relay is running with --inspect enabled (see docker-compose.yml).
  2. Open Chrome DevTools (chrome://inspect) and connect to the relay process.
  3. In the Memory tab, take a Heap Snapshot (Baseline).
  4. Run the load tester.
  5. Wait for the eviction cycle (default: 120s) and take a second Heap Snapshot.
  6. Switch the view to Comparison and select the Baseline snapshot.
  7. Verify that object counts (e.g., WebSocketAdapter, SocketAddress) return to baseline levels.

Server-Side Monitoring

To observe client and subscription counts in real-time during a test, you can instrument src/adapters/web-socket-server-adapter.ts:

  1. Locate the onHeartbeat() method.
  2. 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`);
    }
  3. View the live output via Docker logs:
    docker compose logs -f nostream

Local Quality Checks

Run dead code and dependency analysis before opening a pull request:

npm run knip

npm run lint now runs Biome.

Pull Request Process

  1. Update the relevant documentation with details of changes to the interface, this includes new environment variables, exposed ports, useful file locations and container parameters.
  2. Follow the versioning and changeset process described in Releases & Versioning.
  3. 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.

Releases & Versioning

This project uses Changesets for version management.

For contributors

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 changeset

This interactive prompt will ask you to:

  1. Select the bump type: major, minor, or patch
  2. Write a short summary of the change (this becomes the changelog entry)

The command creates a file in .changeset/ — commit it with your PR.

Empty changesets (no source code changes)

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 --empty

Commit 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)

Release process

  1. Changesets accumulate as PRs are merged to main
  2. The Changesets Release workflow automatically opens a "chore: release new version 🚀" PR that aggregates all pending changesets, bumps package.json, and updates CHANGELOG.md
  3. When a maintainer merges the "chore: release new version 🚀" PR, the workflow publishes a GitHub release and creates the corresponding git tag
  4. The Docker image is then automatically built and pushed to GHCR via the release.yml workflow

Code Quality

Run Biome checks before opening a pull request:

npm run lint
npm run format:check