Skip to content

Commit 2539268

Browse files
chore: merge main, resolve conflicts keeping inlined dependencies
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2 parents bbc4ed2 + 9084e68 commit 2539268

158 files changed

Lines changed: 5928 additions & 4067 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

README.md

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -581,6 +581,52 @@ To see the integration test coverage report open `.coverage/integration/lcov-rep
581581
open .coverage/integration/lcov-report/index.html
582582
```
583583
584+
585+
## Security & Load Testing
586+
587+
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.
588+
589+
### Running the Tester
590+
```bash
591+
# Simulates 5,000 idle "zombie" connections + 100 events/sec spam
592+
npm run test:load -- --zombies 5000 --spam-rate 100
593+
```
594+
595+
### Analyzing Memory (Heap Snapshots)
596+
To verify that connections are being correctly evicted and memory reclaimed:
597+
1. Ensure the relay is running with `--inspect` enabled (see `docker-compose.yml`).
598+
2. Open **Chrome DevTools** (`chrome://inspect`) and connect to the relay process.
599+
3. In the **Memory** tab, take a **Heap Snapshot** (Baseline).
600+
4. Run the load tester.
601+
5. Wait for the eviction cycle (default: 120s) and take a second **Heap Snapshot**.
602+
6. Switch the view to **Comparison** and select the Baseline snapshot.
603+
7. Verify that object counts (e.g., `WebSocketAdapter`, `SocketAddress`) return to baseline levels.
604+
605+
### Server-Side Monitoring
606+
To observe client and subscription counts in real-time during a test, you can instrument `src/adapters/web-socket-server-adapter.ts`:
607+
608+
1. Locate the `onHeartbeat()` method.
609+
2. Add the following logging logic:
610+
```typescript
611+
private onHeartbeat() {
612+
let totalSubs = 0;
613+
let totalClients = 0;
614+
this.webSocketServer.clients.forEach((webSocket) => {
615+
totalClients++;
616+
const webSocketAdapter = this.webSocketsAdapters.get(webSocket) as IWebSocketAdapter;
617+
if (webSocketAdapter) {
618+
webSocketAdapter.emit(WebSocketAdapterEvent.Heartbeat);
619+
totalSubs += webSocketAdapter.getSubscriptions().size;
620+
}
621+
});
622+
console.log(`[HEARTBEAT] Clients: ${totalClients} | Total subscriptions: ${totalSubs} | Heap Used: ${(process.memoryUsage().heapUsed / 1024 / 1024).toFixed(1)} MB`);
623+
}
624+
```
625+
3. View the live output via Docker logs:
626+
```bash
627+
docker compose logs -f nostream
628+
```
629+
=======
584630
## Export Events
585631

586632
Export all stored events to a [JSON Lines](https://jsonlines.org/) (`.jsonl`) file. Each line is a valid NIP-01 Nostr event JSON object. The export streams rows from the database using cursors, so it works safely on relays with millions of events without loading them into memory.
@@ -629,6 +675,7 @@ Delete only selected kinds older than N days:
629675
By default, the script asks for explicit confirmation (`Type 'DELETE' to confirm`).
630676
Use `--force` to skip the prompt.
631677

678+
632679
## Configuration
633680

634681
You can change the default folder by setting the `NOSTR_CONFIG_DIR` environment variable to a different path.

package-lock.json

Lines changed: 3 additions & 20 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848
"cover:unit": "nyc --report-dir .coverage/unit npm run test:unit",
4949
"docker:build": "docker build -t nostream .",
5050
"pretest:integration": "mkdir -p .test-reports/integration",
51+
"test:load": "node -r ts-node/register ./scripts/security-load-test.ts",
5152
"test:integration": "cucumber-js",
5253
"cover:integration": "nyc --report-dir .coverage/integration npm run test:integration -- -p cover",
5354
"export": "node --env-file-if-exists=.env -r ts-node/register src/scripts/export-events.ts",
@@ -95,7 +96,7 @@
9596
"@types/express": "4.17.21",
9697
"@types/js-yaml": "4.0.5",
9798
"@types/mocha": "^9.1.1",
98-
"@types/node": "^24.0.0",
99+
"@types/node": "^24.12.2",
99100
"@types/pg": "^8.6.5",
100101
"@types/ramda": "^0.28.13",
101102
"@types/sinon": "^10.0.11",
@@ -144,4 +145,4 @@
144145
"overrides": {
145146
"axios@<0.31.0": ">=0.31.0"
146147
}
147-
}
148+
}

0 commit comments

Comments
 (0)