@@ -6,6 +6,294 @@ before making a change.
66
77Please keep the conversations civil, respectful and focus on the topic being discussed.
88
9+ ## Issue Assignment & Fairness Policy
10+
11+ To keep the project moving fairly for everyone, please follow these guidelines:
12+
13+ - ** Search before submitting.** Before opening a new issue or PR, search existing issues to avoid
14+ duplicates or overlapping work.
15+ - ** All PRs must have an associated issue.** Open or find a relevant issue before starting work.
16+ Starting a PR without a linked issue means your work may not be merged, and it does not grant
17+ automatic assignment of an issue.
18+ - ** Avoid snowball PRs.** Keep pull requests focused. Do not mix unrelated fixes, features, or
19+ changes in a single PR.
20+ - ** Prefer older issues first.** When choosing what to work on, prefer resolving older open issues
21+ before newer ones, unless a blocker exists or the maintainers have explicitly agreed otherwise.
22+ - ** Abandoned assignments.** An issue assigned to a contributor and not worked on for ** 7 days**
23+ (no comments or commits) is considered abandoned, unless the assignee is actively working on it
24+ or has requested an extension from a maintainer.
25+ - ** Extensions and transfers.** Assignments can be explicitly extended or transferred to another
26+ contributor if the original assignee is unresponsive.
27+ - ** Release your assignment.** If you are no longer interested in an issue, please comment to
28+ request being unassigned so others can pick it up.
29+
30+ ## Development Environment Setup
31+
32+ Install Docker Desktop following the [ official guide] ( https://docs.docker.com/desktop/ ) (if you
33+ plan to use Docker). You may have to uninstall Docker on your machine if you installed it using a
34+ different guide.
35+
36+ Clone the repository and enter the directory:
37+
38+ ```
39+ git clone git@github.com:Cameri/nostream.git
40+ cd nostream
41+ ```
42+
43+ Install dependencies (this also sets up Husky pre-commit hooks automatically):
44+
45+ ```
46+ npm install
47+ ```
48+
49+ > ** Important:** Pre-commit hooks installed by Husky run linting and formatting checks on every
50+ > commit. Do ** not** bypass them with ` git commit --no-verify ` . If a hook fails, fix the reported
51+ > issues before committing.
52+
53+ ### Development Quick Start (Docker Compose)
54+
55+ Start the relay (runs in the foreground until stopped with Ctrl+C):
56+
57+ ```
58+ ./scripts/start
59+ ```
60+
61+ ### Development Quick Start (Standalone)
62+
63+ Set the required environment variables (or copy ` .env.example ` to ` .env ` and edit it):
64+
65+ ```
66+ DB_URI="postgresql://postgres:postgres@localhost:5432/nostr_ts_relay_test"
67+ DB_USER=postgres
68+ ```
69+
70+ or:
71+
72+ ```
73+ DB_HOST=localhost
74+ DB_PORT=5432
75+ DB_NAME=nostr_ts_relay
76+ DB_USER=postgres
77+ DB_PASSWORD=postgres
78+ ```
79+
80+ ```
81+ REDIS_URI="redis://default:nostr_ts_relay@localhost:6379"
82+
83+ REDIS_HOST=localhost
84+ REDIS_PORT=6379
85+ REDIS_USER=default
86+ REDIS_PASSWORD=nostr_ts_relay
87+ ```
88+
89+ Generate a long random secret and set ` SECRET ` :
90+
91+ ```
92+ SECRET=aaabbbccc...dddeeefff
93+ # Secret shortened for brevity
94+ ```
95+
96+ Run migrations (at least once and after pulling new changes):
97+
98+ ```
99+ NODE_OPTIONS="-r dotenv/config" npm run db:migrate
100+ ```
101+
102+ Create the ` .nostr ` folder and copy the default settings file:
103+
104+ ```
105+ mkdir .nostr
106+ cp resources/default-settings.yaml .nostr/settings.yaml
107+ ```
108+
109+ Start in development mode:
110+
111+ ```
112+ npm run dev
113+ ```
114+
115+ Or start in production mode:
116+
117+ ```
118+ npm run start
119+ ```
120+
121+ To clean up build, coverage, and test reports:
122+
123+ ```
124+ npm run clean
125+ ```
126+
127+ ## Tests
128+
129+ ### Linting and formatting (Biome)
130+
131+ Run code quality checks with Biome:
132+
133+ ```
134+ npm run lint
135+ npm run lint:fix
136+ npm run format
137+ npm run format:check
138+ ```
139+
140+ ### Unit tests
141+
142+ Change to the project's directory:
143+
144+ ```
145+ cd /path/to/nostream
146+ ```
147+
148+ Run unit tests:
149+
150+ ```
151+ npm run test:unit
152+ ```
153+
154+ Run unit tests in watch mode:
155+
156+ ```
157+ npm run test:unit:watch
158+ ```
159+
160+ Get unit test coverage:
161+
162+ ```
163+ npm run cover:unit
164+ ```
165+
166+ Open the unit test report:
167+
168+ ```
169+ open .test-reports/unit/index.html
170+ ```
171+
172+ Open the unit test coverage report:
173+
174+ ```
175+ open .coverage/unit/lcov-report/index.html
176+ ```
177+
178+ ### Integration tests (Docker Compose)
179+
180+ Change to the project's directory:
181+
182+ ```
183+ cd /path/to/nostream
184+ ```
185+
186+ Run integration tests:
187+
188+ ```
189+ npm run docker:test:integration
190+ ```
191+
192+ Get integration test coverage:
193+
194+ ```
195+ npm run docker:cover:integration
196+ ```
197+
198+ ### Integration tests (Standalone)
199+
200+ Change to the project's directory:
201+
202+ ```
203+ cd /path/to/nostream
204+ ```
205+
206+ Set the following environment variables:
207+
208+ ```
209+ DB_URI="postgresql://postgres:postgres@localhost:5432/nostr_ts_relay_test"
210+
211+ or
212+
213+ DB_HOST=localhost
214+ DB_PORT=5432
215+ DB_NAME=nostr_ts_relay_test
216+ DB_USER=postgres
217+ DB_PASSWORD=postgres
218+ DB_MIN_POOL_SIZE=1
219+ DB_MAX_POOL_SIZE=2
220+ ```
221+
222+ Run the integration tests:
223+
224+ ```
225+ npm run test:integration
226+ ```
227+
228+ Open the integration test report:
229+
230+ ```
231+ open .test-reports/integration/report.html
232+ ```
233+
234+ Get integration test coverage:
235+
236+ ```
237+ npm run cover:integration
238+ ```
239+
240+ Open the integration test coverage report:
241+
242+ ```
243+ open .coverage/integration/lcov-report/index.html
244+ ```
245+
246+ ## Security & Load Testing
247+
248+ Nostream includes a specialized security tester to simulate Slowloris-style connection holding and
249+ event flood (spam) attacks. This is used to verify relay resilience and prevent memory leaks.
250+
251+ ### Running the Tester
252+
253+ ``` bash
254+ # Simulates 5,000 idle "zombie" connections + 100 events/sec spam
255+ npm run test:load -- --zombies 5000 --spam-rate 100
256+ ```
257+
258+ ### Analyzing Memory (Heap Snapshots)
259+
260+ To verify that connections are being correctly evicted and memory reclaimed:
261+
262+ 1 . Ensure the relay is running with ` --inspect ` enabled (see ` docker-compose.yml ` ).
263+ 2 . Open ** Chrome DevTools** (` chrome://inspect ` ) and connect to the relay process.
264+ 3 . In the ** Memory** tab, take a ** Heap Snapshot** (Baseline).
265+ 4 . Run the load tester.
266+ 5 . Wait for the eviction cycle (default: 120s) and take a second ** Heap Snapshot** .
267+ 6 . Switch the view to ** Comparison** and select the Baseline snapshot.
268+ 7 . Verify that object counts (e.g., ` WebSocketAdapter ` , ` SocketAddress ` ) return to baseline levels.
269+
270+ ### Server-Side Monitoring
271+
272+ To observe client and subscription counts in real-time during a test, you can instrument
273+ ` src/adapters/web-socket-server-adapter.ts ` :
274+
275+ 1 . Locate the ` onHeartbeat() ` method.
276+ 2 . Add the following logging logic:
277+ ``` typescript
278+ private onHeartbeat () {
279+ let totalSubs = 0 ;
280+ let totalClients = 0 ;
281+ this .webSocketServer .clients .forEach ((webSocket ) => {
282+ totalClients ++ ;
283+ const webSocketAdapter = this .webSocketsAdapters .get (webSocket ) as IWebSocketAdapter ;
284+ if (webSocketAdapter ) {
285+ webSocketAdapter .emit (WebSocketAdapterEvent .Heartbeat );
286+ totalSubs += webSocketAdapter .getSubscriptions ().size ;
287+ }
288+ });
289+ console .log (` [HEARTBEAT] Clients: ${totalClients } | Total subscriptions: ${totalSubs } | Heap Used: ${(process .memoryUsage ().heapUsed / 1024 / 1024 ).toFixed (1 )} MB ` );
290+ }
291+ ```
292+ 3 . View the live output via Docker logs:
293+ ``` bash
294+ docker compose logs -f nostream
295+ ```
296+
9297## Local Quality Checks
10298
11299Run dead code and dependency analysis before opening a pull request:
@@ -44,15 +332,23 @@ This interactive prompt will ask you to:
44332
45333The command creates a file in ` .changeset/ ` — commit it with your PR.
46334
47- ### Docs-only PRs (no release )
335+ ### Empty changesets (no source code changes )
48336
49- If your PR only updates documentation and should not affect versioning, add an empty changeset:
337+ If your PR ** only** updates documentation, CI/CD configuration, or test coverage — and leaves all
338+ production source code untouched — an empty changeset is acceptable:
50339
51340``` bash
52341npx changeset --empty
53342```
54343
55- Commit the generated ` .changeset/*.md ` file with your PR. This satisfies CI without producing a version bump or changelog entry.
344+ Commit the generated ` .changeset/*.md ` file with your PR. This satisfies CI without producing a
345+ version bump or changelog entry.
346+
347+ This applies to PRs that exclusively contain:
348+
349+ - Documentation updates (README, CONTRIBUTING, CONFIGURATION, etc.)
350+ - CI/CD workflow changes (` .github/ ` files)
351+ - Test additions or improvements (when no source code is changed)
56352
57353### Release process
58354
0 commit comments