Release: simplify nostr-java from 9 modules to 4, WebSocket improvements, and bug fixes#516
Conversation
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>
…StandardWebSocketClient
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)
There was a problem hiding this comment.
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-apimodule 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
developin addition tomain.
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 |
docs/developer/SECURE_CODING.md
Outdated
| * **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 (` |
There was a problem hiding this comment.
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.
| * **Sanitize Logs:** Ensure user input written to logs does not contain newline characters (` |
There was a problem hiding this comment.
Fixed in 6e8a1bf — removed the duplicated bullet and kept the well-formed one.
| | 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 | |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
The table uses standard single-pipe (|) delimiters and renders correctly on GitHub. No changes needed.
| ## Migrating to 2.0.0 | ||
|
|
||
| **Status:** Planned for future release | ||
| **Status:** Released 2026-02-24 |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
| | 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 | |
There was a problem hiding this comment.
This table uses || delimiters, which typically won’t render as a proper Markdown table. Use standard single-pipe table formatting (|) to ensure consistent rendering.
There was a problem hiding this comment.
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) |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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>
There was a problem hiding this comment.
💡 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))); |
There was a problem hiding this comment.
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 👍 / 👎.
There was a problem hiding this comment.
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.
| this.ids = Collections.unmodifiableList(builder.ids); | ||
| this.authors = Collections.unmodifiableList(builder.authors); | ||
| this.kinds = Collections.unmodifiableList(builder.kinds); |
There was a problem hiding this comment.
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 👍 / 👎.
There was a problem hiding this comment.
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(); |
There was a problem hiding this comment.
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 👍 / 👎.
There was a problem hiding this comment.
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>
Summary
core,event,identity,client), removing dead code and consolidating functionality (~25k lines removed)SpringWebSocketClient/StandardWebSocketClientwith unifiedNostrRelayClientfeaturing connection state tracking, Spring Retry, configurable timeouts, andRelayTimeoutExceptionKey changes
nostr-java-api,nostr-java-base,nostr-java-crypto,nostr-java-encryption,nostr-java-util,nostr-java-examplesmodulesnostr-java-core(crypto + utils),nostr-java-event(events + base types),nostr-java-identity(identity + encryption),nostr-java-clientGenericEventandGenericTagEventFilterbuilderKindsutility class for event kind constantsCompletableFuture-based response handlingTest plan
mvn clean test)mvn clean verify)🤖 Generated with Claude Code