All notable changes to this project will be documented in this file.
The format is inspired by Keep a Changelog, and this project adheres to semantic versioning once 1.0.0 is released.
- Dead code cleanup — deleted unused classes:
IContent,JsonContent,Reactionenum,Response,Nip05Content,Nip05ContentDecoder,BaseAuthMessage,GenericMessage,IKey,GenericEventConverter,GenericEventTypeClassifier,GenericEventDecoder,FiltersDecoder,BaseTagDecoder,GenericEventValidator,GenericEventSerializer,GenericEventUpdater,GenericTagQuery,HttpClientProvider,DefaultHttpClientProvider. testAuthMessagetest andGenericEventSupportTestremoved (tested deleted classes).createGenericTagQuery()removed fromEntityFactory(only consumer of deletedGenericTagQuery).
RelayAuthenticationMessageandCanonicalAuthenticationMessagenow extendBaseMessagedirectly (previously extended the now-deletedBaseAuthMessage).BaseKeynow directly implementsSerializable(previously implemented the now-deletedIKeyinterface).Nip05Validatornow createsHttpClientinstances directly via aFunction<Duration, HttpClient>factory (previously used deletedHttpClientProvider/DefaultHttpClientProviderinterface).
This is a major release that implements the full design simplification described in docs/developer/SIMPLIFICATION_PROPOSAL.md, reducing the library from 9 modules with ~180 classes to 4 modules with ~40 classes.
Kindsutility class with staticintconstants for common Nostr event kinds (TEXT_NOTE,SET_METADATA,CONTACT_LIST, etc.) and range-check methods (isReplaceable(),isEphemeral(),isAddressable(),isValid()).GenericTag.of(String code, String... params)factory method for concise tag creation.GenericTag.toArray()returning the NIP-01 wire format["code", "param0", "param1", ...].GenericTagnow stores tag values asList<String>(replacingList<ElementAttribute>), providing direct access viagetParams().EventFilterbuilder API for composable relay filters:.kinds(),.authors(),.since(),.until(),.addTagFilter(),.limit(),.ids().RelayTimeoutException— typed exception replacing silent empty-list returns on relay timeout.ConnectionStateenum (CONNECTING,CONNECTED,RECONNECTING,CLOSED) for WebSocket connection state tracking.NostrRelayClientasync Virtual Thread APIs:connectAsync(...),sendAsync(...), andsubscribeAsync(...).Nip05Validator.validateAsync()andNip05Validator.validateBatch(...)for parallel NIP-05 validation workloads.- Spring Retry support (
@NostrRetryable,@Recover) consolidated directly intoNostrRelayClient.
- Module consolidation — merged 9 modules into 4:
nostr-java-util+nostr-java-crypto→nostr-java-corenostr-java-base+nostr-java-event→nostr-java-eventnostr-java-id+nostr-java-encryption→nostr-java-identitynostr-java-client(unchanged)
GenericEventis now the sole event class. All 39 concrete event subclasses removed. Events are differentiated byint kindinstead of Java type.GenericTagis now the sole tag class. All 17 concrete tag subclasses removed. Tags are a simplecode+List<String> params.GenericEvent.kindchanged fromKindenum to plainint. Builder simplified to.kind(int)only.GenericEvent.tagschanged fromList<BaseTag>toList<GenericTag>.GenericEventimplementsISignabledirectly (no longer extendsBaseEvent).EventMessagenow referencesGenericEventdirectly instead ofIEvent.IDecoder<T>type bound changed fromIDecoder<T extends IElement>to unboundedIDecoder<T>.TagDeserializernow always producesGenericTagwithList<String>params — no registry dispatch.GenericTagSerializersimplified to output[code, param0, param1, ...]directly fromList<String>.GenericEventDeserializersimplified — no subclass dispatch to concrete event types.NostrUtil.bytesToHex()now usesjava.util.HexFormatinstead of hand-rolled hex encoding.NostrUtil.hexToBytes()family of methods now usesjava.util.HexFormat.parseHex()— fails fast on invalid hex instead of silently producing corrupt bytes.- WebSocket client termination detection now uses proper JSON parsing instead of brittle string-prefix matching.
StandardWebSocketClientrenamed toNostrRelayClient.SpringWebSocketClientabsorbed intoNostrRelayClient(single client class with retry support).- Relay subscription callbacks are now dispatched on Virtual Threads to avoid blocking inbound WebSocket processing.
DefaultHttpClientProvidernow uses a shared Virtual Thread executor instead of creating a new executor perHttpClient.- All
synchronizedblocks inNostrRelayClientreplaced withReentrantLockto avoid Virtual Thread pinning. - Configurable max events per request limit (default 10,000) to prevent unbounded memory accumulation.
GenericTag.getCode()NPE — structurally eliminated by removing the dual-path tag architecture.getCode()is now a trivial field accessor with zero NPE risk.- Removed the remaining
synchronizedcleanup block fromNostrRelayClient.send(...), usingReentrantLockconsistently to avoid VT pinning risk. - Relay timeout now throws
RelayTimeoutExceptioninstead of silently returning an empty list, allowing callers to distinguish "no results" from "timed out".
nostr-java-apimodule — all 26 NIP classes (NIP01–NIP99),EventNostr, factory classes, client managers, service layer, and configuration classes.nostr-java-examplesmodule — all 6 example classes.- 39 concrete event subclasses —
TextNoteEvent,DirectMessageEvent,ContactListEvent,ReactionEvent,DeletionEvent,EphemeralEvent,ReplaceableEvent,AddressableEvent, all Calendar/Marketplace/Channel/NostrConnect events, and more. UseGenericEventwith the appropriateint kind. - 17 concrete tag subclasses —
EventTag,PubKeyTag,AddressTag,IdentifierTag,ReferenceTag,HashtagTag,ExpirationTag,UrlTag,SubjectTag,DelegationTag,RelaysTag,NonceTag,PriceTag,EmojiTag,GeohashTag,LabelTag,LabelNamespaceTag,VoteTag. UseGenericTag.of(code, params...). - 27 entity classes —
UserProfile,Profile,ChannelProfile,ZapRequest,ZapReceipt,Reaction, all Cashu entities, all marketplace entities, and more. Kindenum — replaced byKindsutility class with staticintconstants.ElementAttribute— replaced byList<String>inGenericTag.TagRegistry— no longer needed with a single tag class.- Interfaces and abstract classes:
IElement,ITag,IEvent,IGenericElement,IBech32Encodable,Deleteable,BaseEvent,BaseTag. - Annotations:
@Tag,@Event,@Key. - 14 filter classes —
AbstractFilterable,KindFilter,AuthorFilter,SinceFilter,UntilFilter,HashtagTagFilter,AddressTagFilter,GeohashTagFilter,IdentifierTagFilter,ReferencedEventFilter,ReferencedPublicKeyFilter,UrlTagFilter,VoteTagFilter,GenericTagQueryFilter. UseEventFilter.builder(). - Concrete serializers/deserializers —
AddressTagSerializer,ReferenceTagSerializer,ExpirationTagSerializer,IdentifierTagSerializer,RelaysTagSerializer,BaseTagSerializer,AbstractTagSerializer,CalendarEventDeserializer,ClassifiedListingEventDeserializer,CashuTokenSerializer. - Client classes —
WebSocketClientIF,WebSocketClientFactory,SpringWebSocketClientFactory,SpringWebSocketClient. UseNostrRelayClientdirectly. - Dead code —
Markerenum,RelayUrivalue object. - 5 old modules —
nostr-java-util,nostr-java-crypto,nostr-java-base,nostr-java-id,nostr-java-encryption(merged into the 4 remaining modules). - Dead
pollIntervalMsparameter from WebSocket client constructors.
- Configurable WebSocket buffer sizes for handling large Nostr events via
nostr.websocket.max-text-message-buffer-sizeandnostr.websocket.max-binary-message-buffer-sizeproperties.
- No additional behavior changes in this release; Kind APIs and WebSocket concurrency improvements were introduced in 1.2.1.
- No new fixes beyond 1.2.1; this release focuses on configurable WebSocket buffer sizes.
- NIP-44 now correctly uses HKDF-Extract for conversation key derivation, ensuring proper cryptographic key generation.
- WebSocket client now correctly accumulates all relay responses (EVENT messages) before completing, waiting for termination signals (EOSE, OK, NOTICE, CLOSED) instead of returning after the first message.
- WebSocket client thread-safety improved by encapsulating pending request state, preventing potential race conditions when multiple threads call send() concurrently.
- WebSocket client now rejects concurrent send() calls with IllegalStateException instead of silently orphaning the previous request's future.
- KindFilter and ClassifiedListingEventDeserializer now use Kind.valueOfStrict() for fail-fast deserialization of unknown kind values.
- Kind.valueOf(int) now returns null for unknown kind values instead of throwing, allowing graceful handling of custom or future NIP kinds during JSON deserialization.
- Added Kind.valueOfStrict(int) for callers who need fail-fast behavior on unknown kinds.
- Added Kind.findByValue(int) returning Optional for safe, explicit handling of unknown kinds.
- NIP-44 encryption now correctly uses HKDF instead of PBKDF2 for key derivation, as required by the specification. This fix enables DM interoperability between Java backend and JavaScript frontend implementations (e.g., nostr-tools).
- Switched integration tests to use strfry relay for improved robustness.
- Removed AGENTS.md and CLAUDE.md documentation files from the repository.
- StandardWebSocketClient now configures WebSocketContainer with a 1-hour idle timeout (configurable via
nostr.websocket.max-idle-timeout-ms) to prevent premature connection closures when relays have periods of inactivity.
- Public constructor
StandardWebSocketClient(String relayUri, long awaitTimeoutMs, long pollIntervalMs)for programmatic timeout configuration outside Spring DI context.
- Enhanced diagnostic logging for timeout configuration in StandardWebSocketClient.
- Simplified WebSocket client initialization and retry logic in tests.
- Updated
JsonDeserializebuilder reference in API module.
- Updated project version and added artifact names in POM files.
- Added Sonatype Central server credentials configuration.
- Updated Maven command for central publishing.
- Release automation script
scripts/release.shwith bump/tag/verify/publish/next-snapshot commands (supports--no-docker,--skip-tests, and--dry-run). - GitHub Actions:
- CI workflow
.github/workflows/ci.ymlwith Java 21 build and Java 17 POM validation; separate Docker-based integration job; uploads reports/artifacts. - Release workflow
.github/workflows/release.ymlpublishing to Maven Central, validating tag vs POM version, and creating GitHub releases.
- CI workflow
- Documentation:
docs/explanation/dependency-alignment.md— BOM alignment and post-1.0 override removal plan.docs/howto/version-uplift-workflow.md— step-by-step release process; wired toscripts/release.sh.
- Roadmap project helper
scripts/create-roadmap-project.shnow adds tasks for:- Release workflow secrets setup (Central + GPG)
- Enforcing tag/version parity during releases
- Updating docs version references to latest
- CI + Docker IT stability and triage plan
- Expanded decoder and mapping tests to cover all implemented relay commands (EVENT, CLOSE, EOSE, NOTICE, OK, AUTH).
- Stabilized NIP-52 (calendar) and NIP-99 (classifieds) integration tests for deterministic relay behavior.
- Docs updates to prefer BOM usage:
docs/GETTING_STARTED.mdupdated with Maven/Gradle BOM examplesdocs/howto/use-nostr-java-api.mdupdated to import BOM and omit per-module versions- Cross-links added from the roadmap to migration and dependency alignment docs
- README cleanup: removed maintainer-only roadmap automation and moved troubleshooting to
docs/howto/diagnostics.md.
- Deprecated APIs finalized for 1.0.0:
nostr.config.Constants.Kindfacade — usenostr.base.Kindnostr.base.Encoder.ENCODER_MAPPER_BLACKBIRD— usenostr.event.json.EventJsonMapper#getMapper()nostr.api.NIP01#createTextNoteEvent(Identity, String)and related Identity-based overloads — use instance-configured sendernostr.api.NIP61#createNutzapEvent(Amount, List<CashuProof>, URL, List<EventTag>, PublicKey, String)— use slimmer overload and add amount/unit viaNIP60nostr.event.tag.GenericTag(String, Integer)compatibility ctornostr.id.EntityFactory.Events#createGenericTag(PublicKey, IEvent, Integer)
- Integration tests require Docker (Testcontainers). CI runs a separate job for them on push; PRs use the no-Docker profile.
- See
MIGRATION.mdfor complete guidance on deprecated API replacements.