Skip to content

Commit 5f2dc4c

Browse files
committed
Merge branch 'main' into feature/35-I2P-support-addition
2 parents 4af3eac + 26bcdd5 commit 5f2dc4c

26 files changed

Lines changed: 2992 additions & 1386 deletions

.eslintignore

Lines changed: 0 additions & 5 deletions
This file was deleted.

.eslintrc.js

Lines changed: 0 additions & 53 deletions
This file was deleted.

.github/workflows/checks.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ jobs:
4040
cache: npm
4141
- name: Install package dependencies
4242
run: npm ci
43-
- name: Run ESLint
43+
- name: Run Biome
4444
run: npm run lint
4545
- name: Run Knip
4646
run: npm run knip
@@ -56,7 +56,7 @@ jobs:
5656
cache: npm
5757
- name: Install package dependencies
5858
run: npm ci
59-
- name: Run ESLint
59+
- name: Run build check
6060
run: npm run build:check
6161
compose-validate:
6262
name: Validate Docker Compose overlays (Tor / I2P)

.github/workflows/pr-title.yml

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
name: PR Title Check
2+
3+
on:
4+
pull_request:
5+
types:
6+
- opened
7+
- edited
8+
- synchronize
9+
- reopened
10+
11+
jobs:
12+
pr-title-lint:
13+
name: Lint PR title
14+
runs-on: ubuntu-latest
15+
steps:
16+
- uses: amannn/action-semantic-pull-request@v5
17+
env:
18+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
19+
with:
20+
requireScope: false
21+
subjectPattern: ^.+$
22+
subjectPatternError: PR title must follow Conventional Commits format (e.g. "feat: add feature" or "fix(scope): fix bug").

CONTRIBUTING.md

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ Run dead code and dependency analysis before opening a pull request:
1414
npm run knip
1515
```
1616

17-
`npm run lint` now runs Knip first, then ESLint.
17+
`npm run lint` now runs Biome.
1818

1919
## Pull Request Process
2020

@@ -24,3 +24,12 @@ npm run knip
2424
Pull Request would represent. The versioning scheme we use is [SemVer](http://semver.org/).
2525
3. You may merge the Pull Request in once you have the sign-off of two other developers, or if you
2626
do not have permission to do that, you may request the second reviewer to merge it for you.
27+
28+
## Code Quality
29+
30+
Run Biome checks before opening a pull request:
31+
32+
```
33+
npm run lint
34+
npm run format:check
35+
```

README.md

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -486,6 +486,17 @@ Start:
486486
487487
## Tests
488488
489+
### Linting and formatting (Biome)
490+
491+
Run code quality checks with Biome:
492+
493+
```
494+
npm run lint
495+
npm run lint:fix
496+
npm run format
497+
npm run format:check
498+
```
499+
489500
### Unit tests
490501
491502
Open a terminal and change to the project's directory:
@@ -586,6 +597,52 @@ To see the integration test coverage report open `.coverage/integration/lcov-rep
586597
open .coverage/integration/lcov-report/index.html
587598
```
588599
600+
601+
## Security & Load Testing
602+
603+
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.
604+
605+
### Running the Tester
606+
```bash
607+
# Simulates 5,000 idle "zombie" connections + 100 events/sec spam
608+
npm run test:load -- --zombies 5000 --spam-rate 100
609+
```
610+
611+
### Analyzing Memory (Heap Snapshots)
612+
To verify that connections are being correctly evicted and memory reclaimed:
613+
1. Ensure the relay is running with `--inspect` enabled (see `docker-compose.yml`).
614+
2. Open **Chrome DevTools** (`chrome://inspect`) and connect to the relay process.
615+
3. In the **Memory** tab, take a **Heap Snapshot** (Baseline).
616+
4. Run the load tester.
617+
5. Wait for the eviction cycle (default: 120s) and take a second **Heap Snapshot**.
618+
6. Switch the view to **Comparison** and select the Baseline snapshot.
619+
7. Verify that object counts (e.g., `WebSocketAdapter`, `SocketAddress`) return to baseline levels.
620+
621+
### Server-Side Monitoring
622+
To observe client and subscription counts in real-time during a test, you can instrument `src/adapters/web-socket-server-adapter.ts`:
623+
624+
1. Locate the `onHeartbeat()` method.
625+
2. Add the following logging logic:
626+
```typescript
627+
private onHeartbeat() {
628+
let totalSubs = 0;
629+
let totalClients = 0;
630+
this.webSocketServer.clients.forEach((webSocket) => {
631+
totalClients++;
632+
const webSocketAdapter = this.webSocketsAdapters.get(webSocket) as IWebSocketAdapter;
633+
if (webSocketAdapter) {
634+
webSocketAdapter.emit(WebSocketAdapterEvent.Heartbeat);
635+
totalSubs += webSocketAdapter.getSubscriptions().size;
636+
}
637+
});
638+
console.log(`[HEARTBEAT] Clients: ${totalClients} | Total subscriptions: ${totalSubs} | Heap Used: ${(process.memoryUsage().heapUsed / 1024 / 1024).toFixed(1)} MB`);
639+
}
640+
```
641+
3. View the live output via Docker logs:
642+
```bash
643+
docker compose logs -f nostream
644+
```
645+
=======
589646
## Export Events
590647

591648
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.
@@ -634,6 +691,7 @@ Delete only selected kinds older than N days:
634691
By default, the script asks for explicit confirmation (`Type 'DELETE' to confirm`).
635692
Use `--force` to skip the prompt.
636693

694+
637695
## Configuration
638696

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

biome.json

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
{
2+
"$schema": "https://biomejs.dev/schemas/2.4.11/schema.json",
3+
"vcs": { "enabled": true, "clientKind": "git", "useIgnoreFile": true },
4+
"files": {
5+
"ignoreUnknown": false,
6+
"includes": [
7+
"**",
8+
"!**/node_modules/**",
9+
"!**/dist/**",
10+
"!**/.test-reports/**",
11+
"!**/.coverage/**",
12+
"!**/.nostr/**",
13+
"!**/tslint.json"
14+
]
15+
},
16+
"formatter": {
17+
"enabled": true,
18+
"indentStyle": "space",
19+
"lineWidth": 120
20+
},
21+
"linter": {
22+
"enabled": true,
23+
"rules": {
24+
"recommended": false,
25+
"correctness": { "noUnusedVariables": "error" },
26+
"style": { "useBlockStatements": "warn" },
27+
"suspicious": { "noExplicitAny": "off" }
28+
}
29+
},
30+
"javascript": {
31+
"formatter": {
32+
"quoteStyle": "single",
33+
"semicolons": "asNeeded",
34+
"trailingCommas": "all"
35+
}
36+
}
37+
}

0 commit comments

Comments
 (0)