Skip to content

Release: simplify nostr-java from 9 modules to 4, WebSocket improvements, and bug fixes#516

Merged
tcheeric merged 20 commits intomainfrom
develop
Feb 24, 2026
Merged

Release: simplify nostr-java from 9 modules to 4, WebSocket improvements, and bug fixes#516
tcheeric merged 20 commits intomainfrom
develop

Conversation

@tcheeric
Copy link
Owner

Summary

  • Major refactoring: Simplified nostr-java from 9 modules to 4 (core, event, identity, client), removing dead code and consolidating functionality (~25k lines removed)
  • WebSocket client overhaul: Replaced SpringWebSocketClient/StandardWebSocketClient with unified NostrRelayClient featuring connection state tracking, Spring Retry, configurable timeouts, and RelayTimeoutException
  • NIP-44 fix: Corrected key derivation to use HKDF instead of PBKDF2
  • CI: Enabled CI to run on develop branch in addition to main
  • Version bump: 1.2.1 → 1.3.0

Key changes

  • Removed nostr-java-api, nostr-java-base, nostr-java-crypto, nostr-java-encryption, nostr-java-util, nostr-java-examples modules
  • Consolidated into nostr-java-core (crypto + utils), nostr-java-event (events + base types), nostr-java-identity (identity + encryption), nostr-java-client
  • Replaced typed event subclasses and typed tags with GenericEvent and GenericTag
  • Replaced filter subclasses with unified EventFilter builder
  • Added Kinds utility class for event kind constants
  • Improved WebSocket thread-safety with CompletableFuture-based response handling

Test plan

  • All unit tests pass (mvn clean test)
  • Integration tests pass (mvn clean verify)
  • CI pipeline passes on PR

🤖 Generated with Claude Code

tcheeric and others added 18 commits January 25, 2026 18:02
Configure WebSocketContainer with larger text/binary message buffer
sizes (default 1MB each) to handle NIP-60 wallet state events and
other large Nostr events that exceed default buffer sizes.

Buffer sizes are now configurable via system properties:
- nostr.websocket.max-idle-timeout-ms (default: 3600000)
- nostr.websocket.max-text-message-buffer-size (default: 1048576)
- nostr.websocket.max-binary-message-buffer-size (default: 1048576)

This fixes 1009 "message too big" errors that caused WebSocket
disconnections when receiving large events from relays.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Projects updated:
- nostr-java: 1.2.1 → 1.3.0 (minor)
- All modules updated to match parent version

Changes include:
- Added configurable WebSocket buffer sizes for large Nostr events
- Improved Kind enum with safer lookup methods
- Fixed thread-safety in WebSocket client send()
- Fixed fail-fast deserialization in KindFilter

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
[WIP] Update configurable WebSocket buffer sizes based on feedback
[WIP] Apply feedback from review on configurable WebSocket buffer sizes
[WIP] Update configurable WebSocket buffer sizes for Nostr events
[WIP] Update configurable WebSocket buffer sizes for Nostr events
Add configurable WebSocket buffer sizes for large Nostr events
Implements the full design simplification (2.0.0):
- Merge 9 modules into 4: core, event, identity, client
- Remove 39 concrete event subclasses, 17 tag subclasses, 27 entities
- GenericEvent as sole event class, GenericTag with List<String> params
- Kinds utility replaces Kind enum, EventFilter builder replaces 14 filters
- NostrRelayClient with Virtual Threads, async APIs, Spring Retry
- RelayTimeoutException replaces silent empty-list returns
- java.util.HexFormat replaces hand-rolled hex encoding
- Delete 21 additional dead classes (IContent, GenericEventConverter, etc.)
- Merge IKey into BaseKey, BaseAuthMessage into BaseMessage hierarchy
- Overhaul all documentation for 2.0 architecture

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Fix SECURE_CODING.md: replace "imani-bridge" with "nostr-java" and
  fix broken markdown escaping for \n, \r characters
- Fix GenericTagDecoder: preserve duplicate tag parameters (Nostr tags
  are positional arrays where repeated values are valid data)
- Fix EventFilter: deep-copy tagFilters map in constructor to prevent
  mutable state leaking from reused builders
- Fix NostrRelayClient: log warning when max-events-per-request limit
  is reached instead of silently dropping events

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
refactor: simplify nostr-java from 9 modules to 4 (v2.0.0)
@tcheeric tcheeric requested a review from Copilot February 24, 2026 22:38
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Refactors and simplifies the project’s public surface (modules, clients, docs), removing the legacy nostr-java-api module and updating documentation/CI to reflect the new module layout and NostrRelayClient usage.

Changes:

  • Removes the legacy nostr-java-api module code (NIP helpers, factories, config) and its unit/integration tests.
  • Updates docs to reference nostr-java-client + NostrRelayClient, and documents updated WebSocket settings/retry/virtual-thread behavior.
  • Expands CI triggers to run on develop in addition to main.

Reviewed changes

Copilot reviewed 136 out of 467 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
nostr-java-api/src/test/java/nostr/api/unit/NIP42Test.java Removes legacy API module unit test
nostr-java-api/src/test/java/nostr/api/unit/NIP40Test.java Removes legacy API module unit test
nostr-java-api/src/test/java/nostr/api/unit/NIP32Test.java Removes legacy API module unit test
nostr-java-api/src/test/java/nostr/api/unit/NIP31Test.java Removes legacy API module unit test
nostr-java-api/src/test/java/nostr/api/unit/NIP30Test.java Removes legacy API module unit test
nostr-java-api/src/test/java/nostr/api/unit/NIP28Test.java Removes legacy API module unit test
nostr-java-api/src/test/java/nostr/api/unit/NIP25Test.java Removes legacy API module unit test
nostr-java-api/src/test/java/nostr/api/unit/NIP23Test.java Removes legacy API module unit test
nostr-java-api/src/test/java/nostr/api/unit/NIP20Test.java Removes legacy API module unit test
nostr-java-api/src/test/java/nostr/api/unit/NIP15Test.java Removes legacy API module unit test
nostr-java-api/src/test/java/nostr/api/unit/NIP14Test.java Removes legacy API module unit test
nostr-java-api/src/test/java/nostr/api/unit/NIP12Test.java Removes legacy API module unit test
nostr-java-api/src/test/java/nostr/api/unit/NIP09Test.java Removes legacy API module unit test
nostr-java-api/src/test/java/nostr/api/unit/NIP05Test.java Removes legacy API module unit test
nostr-java-api/src/test/java/nostr/api/unit/NIP04Test.java Removes legacy API module unit tests for encryption
nostr-java-api/src/test/java/nostr/api/unit/NIP03Test.java Removes legacy API module unit test
nostr-java-api/src/test/java/nostr/api/unit/NIP02Test.java Removes legacy API module unit test
nostr-java-api/src/test/java/nostr/api/unit/NIP01MessagesTest.java Removes legacy API module message-encoding tests
nostr-java-api/src/test/java/nostr/api/unit/NIP01EventBuilderTest.java Removes legacy builder test
nostr-java-api/src/test/java/nostr/api/unit/ConstantsTest.java Removes legacy constants tests
nostr-java-api/src/test/java/nostr/api/unit/Bolt11UtilTest.java Removes legacy Bolt11Util unit tests
nostr-java-api/src/test/java/nostr/api/integration/support/FakeWebSocketClientFactory.java Removes legacy integration test helper
nostr-java-api/src/test/java/nostr/api/integration/support/FakeWebSocketClient.java Removes legacy integration test helper
nostr-java-api/src/test/java/nostr/api/integration/ZDoLastApiNIP09EventIT.java Removes legacy relay integration test
nostr-java-api/src/test/java/nostr/api/integration/SubscriptionLifecycleIT.java Removes legacy subscription lifecycle integration test
nostr-java-api/src/test/java/nostr/api/integration/NostrSpringWebSocketClientSubscriptionIT.java Removes legacy subscription integration test
nostr-java-api/src/test/java/nostr/api/integration/MultiRelayIT.java Removes legacy multi-relay integration test
nostr-java-api/src/test/java/nostr/api/integration/BaseRelayIntegrationTest.java Removes legacy Testcontainers relay base test
nostr-java-api/src/test/java/nostr/api/integration/ApiNIP99EventIT.java Removes legacy NIP event integration test
nostr-java-api/src/test/java/nostr/api/integration/ApiNIP52EventIT.java Removes legacy NIP event integration test
nostr-java-api/src/test/java/nostr/api/integration/ApiEventTestUsingSpringWebSocketClientIT.java Removes legacy integration tests relying on removed clients
nostr-java-api/src/test/java/nostr/api/client/WebSocketHandlerSendRequestTest.java Removes legacy handler tests
nostr-java-api/src/test/java/nostr/api/client/WebSocketHandlerSendCloseFrameTest.java Removes legacy handler tests
nostr-java-api/src/test/java/nostr/api/client/WebSocketHandlerRequestErrorTest.java Removes legacy handler tests
nostr-java-api/src/test/java/nostr/api/client/WebSocketHandlerCloseSequencingTest.java Removes legacy handler tests
nostr-java-api/src/test/java/nostr/api/client/WebSocketHandlerCloseIdempotentTest.java Removes legacy handler tests
nostr-java-api/src/test/java/nostr/api/client/README.md Removes legacy test-suite documentation
nostr-java-api/src/test/java/nostr/api/client/NostrSubscriptionManagerCloseTest.java Removes legacy subscription manager tests
nostr-java-api/src/test/java/nostr/api/client/NostrSpringWebSocketClientSubscribeLoggingTest.java Removes legacy logging tests
nostr-java-api/src/test/java/nostr/api/client/NostrSpringWebSocketClientRelaysTest.java Removes legacy relays tests
nostr-java-api/src/test/java/nostr/api/client/NostrSpringWebSocketClientLoggingTest.java Removes legacy logging tests
nostr-java-api/src/test/java/nostr/api/client/NostrSpringWebSocketClientHandlerIntegrationTest.java Removes legacy handler integration test
nostr-java-api/src/test/java/nostr/api/client/NostrSpringWebSocketClientCloseLoggingTest.java Removes legacy close logging tests
nostr-java-api/src/test/java/nostr/api/client/NostrRequestDispatcherTest.java Removes legacy dispatcher tests
nostr-java-api/src/test/java/nostr/api/client/NostrRequestDispatcherEnsureClientsTest.java Removes legacy dispatcher tests
nostr-java-api/src/test/java/nostr/api/TestableWebSocketClientHandler.java Removes legacy test-only handler subclass
nostr-java-api/src/test/java/nostr/api/TestHandlerFactory.java Removes legacy test-only factory
nostr-java-api/src/test/java/nostr/api/NIP46RequestTest.java Removes legacy NIP46 request test
nostr-java-api/src/main/resources/relays.properties Removes legacy relay properties file
nostr-java-api/src/main/resources/app.properties Removes legacy app properties file
nostr-java-api/src/main/java/nostr/config/RelaysProperties.java Removes legacy Spring config properties type
nostr-java-api/src/main/java/nostr/config/RelayConfig.java Removes legacy relay Spring configuration
nostr-java-api/src/main/java/nostr/config/Constants.java Removes legacy constants container
nostr-java-api/src/main/java/nostr/api/service/impl/DefaultNoteService.java Removes legacy note-dispatch service
nostr-java-api/src/main/java/nostr/api/service/NoteService.java Removes legacy note service interface
nostr-java-api/src/main/java/nostr/api/nip57/ZapRequestParameters.java Removes legacy zap builder parameter object
nostr-java-api/src/main/java/nostr/api/nip57/NIP57ZapRequestBuilder.java Removes legacy zap request event builder
nostr-java-api/src/main/java/nostr/api/nip57/NIP57ZapReceiptBuilder.java Removes legacy zap receipt builder
nostr-java-api/src/main/java/nostr/api/nip57/NIP57TagFactory.java Removes legacy tag factory
nostr-java-api/src/main/java/nostr/api/nip57/Bolt11Util.java Removes legacy BOLT11 parsing utility
nostr-java-api/src/main/java/nostr/api/nip01/NIP01TagFactory.java Removes legacy tag factory
nostr-java-api/src/main/java/nostr/api/nip01/NIP01MessageFactory.java Removes legacy message factory
nostr-java-api/src/main/java/nostr/api/nip01/NIP01EventBuilder.java Removes legacy event builder
nostr-java-api/src/main/java/nostr/api/factory/impl/GenericEventFactory.java Removes legacy event factory
nostr-java-api/src/main/java/nostr/api/factory/impl/EventMessageFactory.java Removes legacy message factory
nostr-java-api/src/main/java/nostr/api/factory/impl/BaseTagFactory.java Removes legacy tag factory
nostr-java-api/src/main/java/nostr/api/factory/MessageFactory.java Removes legacy message factory abstraction
nostr-java-api/src/main/java/nostr/api/factory/EventFactory.java Removes legacy event factory base
nostr-java-api/src/main/java/nostr/api/factory/BaseMessageFactory.java Removes legacy base message factory
nostr-java-api/src/main/java/nostr/api/client/WebSocketClientHandlerFactory.java Removes legacy handler factory
nostr-java-api/src/main/java/nostr/api/client/NostrSubscriptionManager.java Removes legacy subscription manager
nostr-java-api/src/main/java/nostr/api/client/NostrRequestDispatcher.java Removes legacy request dispatcher
nostr-java-api/src/main/java/nostr/api/client/NostrRelayRegistry.java Removes legacy relay handler registry
nostr-java-api/src/main/java/nostr/api/client/NostrEventDispatcher.java Removes legacy event dispatcher
nostr-java-api/src/main/java/nostr/api/NostrIF.java Removes legacy high-level client interface
nostr-java-api/src/main/java/nostr/api/NIP99.java Removes legacy NIP helper
nostr-java-api/src/main/java/nostr/api/NIP65.java Removes legacy NIP helper
nostr-java-api/src/main/java/nostr/api/NIP61.java Removes legacy NIP helper
nostr-java-api/src/main/java/nostr/api/NIP46.java Removes legacy NIP helper
nostr-java-api/src/main/java/nostr/api/NIP42.java Removes legacy NIP helper
nostr-java-api/src/main/java/nostr/api/NIP40.java Removes legacy NIP helper
nostr-java-api/src/main/java/nostr/api/NIP32.java Removes legacy NIP helper
nostr-java-api/src/main/java/nostr/api/NIP31.java Removes legacy NIP helper
nostr-java-api/src/main/java/nostr/api/NIP30.java Removes legacy NIP helper
nostr-java-api/src/main/java/nostr/api/NIP28.java Removes legacy NIP helper
nostr-java-api/src/main/java/nostr/api/NIP25.java Removes legacy NIP helper
nostr-java-api/src/main/java/nostr/api/NIP23.java Removes legacy NIP helper
nostr-java-api/src/main/java/nostr/api/NIP20.java Removes legacy NIP helper
nostr-java-api/src/main/java/nostr/api/NIP15.java Removes legacy NIP helper
nostr-java-api/src/main/java/nostr/api/NIP14.java Removes legacy NIP helper
nostr-java-api/src/main/java/nostr/api/NIP12.java Removes legacy NIP helper
nostr-java-api/src/main/java/nostr/api/NIP09.java Removes legacy NIP helper
nostr-java-api/src/main/java/nostr/api/NIP05.java Removes legacy NIP helper
nostr-java-api/src/main/java/nostr/api/NIP03.java Removes legacy NIP helper
nostr-java-api/src/main/java/nostr/api/NIP02.java Removes legacy NIP helper
nostr-java-api/src/main/java/nostr/api/EventNostr.java Removes legacy event-oriented client wrapper
nostr-java-api/pom.xml Removes legacy module Maven configuration
docs/operations/configuration.md Updates operational docs for NostrRelayClient settings/retry/vthreads
docs/howto/version-uplift-workflow.md Updates dependency artifactId examples to nostr-java-client
docs/howto/use-nostr-java-api.md Updates quickstart docs to nostr-java-client + NostrRelayClient
docs/howto/streaming-subscriptions.md Updates subscription docs to new filter builder + NostrRelayClient APIs
docs/howto/diagnostics.md Updates diagnostics guidance to new client error model and multi-relay patterns
docs/howto/custom-events.md Updates custom event docs to GenericEvent.builder & GenericTag
docs/explanation/roadmap-1.0.md Marks roadmap as historical and references simplification
docs/explanation/dependency-alignment.md Updates BOM/module alignment guidance to the simplified module layout
docs/developer/SECURE_CODING.md Adds secure coding guidelines documentation
docs/README.md Refreshes docs index to match new guides/modules
docs/GETTING_STARTED.md Updates getting started dependencies to nostr-java-client
docs/CODEBASE_OVERVIEW.md Updates module overview and WebSocket configuration docs
MIGRATION.md Expands migration guide with major-version changes and examples
.github/workflows/ci.yml Runs CI on develop as well as main

* **APIs:** Use Java API equivalents (e.g., `java.nio.file`) instead of shell commands (e.g., `ls`, `rm`).

### Log Injection
* **Sanitize Logs:** Ensure user input written to logs does not contain newline characters (`
Copy link

Copilot AI Feb 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There’s a duplicated bullet and the first line has a broken inline-code opener (`) which will render incorrectly. Remove the duplicate and keep a single, well-formed bullet (with properly closed inline code) so the section renders cleanly.

Suggested change
* **Sanitize Logs:** Ensure user input written to logs does not contain newline characters (`

Copilot uses AI. Check for mistakes.
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 6e8a1bf — removed the duplicated bullet and kept the well-formed one.

Comment on lines +21 to +26
| Range | Type | Description |
|-------|------|-------------|
| 0-9999 | Regular | Standard events, can be deleted |
| 10000-19999 | Replaceable | Newer event replaces older (by pubkey) |
| 20000-29999 | Ephemeral | Not stored by relays |
| 30000-39999 | Parameterized Replaceable | Replaceable with `d` tag parameter |
Copy link

Copilot AI Feb 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This table uses double pipes (||) which is not standard Markdown table syntax and likely won’t render as intended. Convert it to a normal pipe table (single | delimiters) so it renders consistently on GitHub.

Copilot uses AI. Check for mistakes.
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The table uses standard single-pipe (|) delimiters and renders correctly on GitHub. No changes needed.

Comment on lines +14 to +16
## Migrating to 2.0.0

**Status:** Planned for future release
**Status:** Released 2026-02-24
Copy link

Copilot AI Feb 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This migration guide targets 2.0.0, but the PR metadata/version bump indicates a 1.3.0 release. Either update the migration section to match the actual release version, or adjust the PR metadata/versioning so docs and release numbering are consistent.

Copilot uses AI. Check for mistakes.
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The project version in pom.xml is 2.0.0, so the migration guide is correct. The 1.3.0 version bump was an intermediate step on the develop branch; the final release version is 2.0.0.

Comment on lines +22 to +29
| Old Module | New Module | Notes |
|---|---|---|
| `nostr-java-util` + `nostr-java-crypto` | `nostr-java-core` | Merged |
| `nostr-java-base` + `nostr-java-event` | `nostr-java-event` | Merged |
| `nostr-java-id` + `nostr-java-encryption` | `nostr-java-identity` | Merged |
| `nostr-java-client` | `nostr-java-client` | Unchanged |
| `nostr-java-api` | (removed) | Use `GenericEvent` directly |
| `nostr-java-examples` | (removed) | See docs/howto guides |
Copy link

Copilot AI Feb 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This table uses || delimiters, which typically won’t render as a proper Markdown table. Use standard single-pipe table formatting (|) to ensure consistent rendering.

Copilot uses AI. Check for mistakes.
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The table uses standard single-pipe (|) delimiters and renders correctly. No changes needed.

This document explains how nostr-java aligns dependency versions across modules and how the BOM manages consumer dependencies.

Purpose: ensure consistent, reproducible builds across all modules (api, client, event, etc.) and for consumers, with clear steps to remove temporary overrides once the BOM includes 1.0.0.
## Current state (2.0.0)
Copy link

Copilot AI Feb 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This section hard-codes 2.0.0 as the current state, which appears inconsistent with the PR’s stated release/version bump. Consider making this version-agnostic (e.g., 'Current state') or aligning it to the release version referenced by this PR to avoid confusing consumers.

Copilot uses AI. Check for mistakes.
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The project version in pom.xml is 2.0.0, which matches the docs. No change needed.

…_CODING.md

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: f662bdffbb

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

.filter(GenericTag.class::isInstance)
.map(GenericTag.class::cast)
.filter(t -> t.getCode().equals(tagCode))
.anyMatch(t -> t.getParams().stream().anyMatch(values::contains)));

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Restrict tag-filter matching to the tag value position

In EventFilter.toPredicate, tag filters currently match if any parameter in a matching tag contains a requested value, which makes #e/#p filters match relay URLs or marker fields instead of just the tag’s value slot (e.g., "reply" in an e tag can satisfy a #e filter). This broadens predicate results and can return unrelated events for common multi-parameter tags; matching should be constrained to the first parameter for NIP-01-style tag-value filters.

Useful? React with 👍 / 👎.

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 87ba1a0 — tag filter now matches only the first parameter (value position at index 0) per NIP-01 semantics, so relay URLs and markers in multi-parameter tags like e and p won't produce false matches.

Comment on lines 43 to 45
this.ids = Collections.unmodifiableList(builder.ids);
this.authors = Collections.unmodifiableList(builder.authors);
this.kinds = Collections.unmodifiableList(builder.kinds);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Defensively copy builder lists before storing EventFilter

EventFilter stores builder.ids/authors/kinds by wrapping the same mutable lists in Collections.unmodifiableList, so reusing a builder after one build() mutates previously built filters through shared list instances. That breaks immutability assumptions and can silently change active query criteria if callers keep using the same builder instance.

Useful? React with 👍 / 👎.

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 87ba1a0 — EventFilter constructor now defensively copies the builder's lists via new ArrayList<>(...) before wrapping in Collections.unmodifiableList, so reusing a builder won't mutate previously built filters.

if (open) {
clientSession.close();
connectionState.set(ConnectionState.CLOSED);
notifyClose();

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Prevent duplicate close notifications in relay client

NostrRelayClient.close() invokes notifyClose() directly even though afterConnectionClosed also invokes notifyClose(), so when a normal clientSession.close() triggers the close callback, listeners can be notified twice. This can double-run teardown logic (e.g., decrement latches or release resources twice) and is a regression from the prior guarded close-notification flow.

Useful? React with 👍 / 👎.

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 87ba1a0 — removed the explicit notifyClose() and connectionState.set(CLOSED) from close(). The afterConnectionClosed callback already handles both, so close() now just calls clientSession.close() and lets the callback do the rest.

- Restrict tag-filter matching to first parameter (value position) per NIP-01
- Defensively copy builder lists in EventFilter constructor to prevent
  shared mutable state when reusing builders
- Remove duplicate notifyClose() call from close() since
  afterConnectionClosed already handles listener notification

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@tcheeric tcheeric merged commit 50983c0 into main Feb 24, 2026
10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants