Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
625dfce
chore: tidy spring websocket client files
tcheeric Oct 3, 2025
2d6a242
docs: add spring subscription example client
tcheeric Oct 3, 2025
924599e
fix: avoid blocking subscription close
tcheeric Oct 3, 2025
4b0a175
chore: add .qodana to gitignore
Oct 3, 2025
932d643
refactor(api): enable non-blocking subscription handling
Oct 3, 2025
d2635dc
refactor(api): restore override for decode method
Oct 3, 2025
14ad06b
docs(bech32): improve method documentation for encode and decode
Oct 3, 2025
e34c7c8
refactor(api): make classTypeTagsMap final for immutability
Oct 3, 2025
5a521f2
docs(schnorr): enhance method documentation for sign and verify
Oct 3, 2025
ab99fe4
refactor(api): remove unused id field from ZapRequest
Oct 3, 2025
b811357
refactor(api): remove redundant assertion handling in product event v…
Oct 3, 2025
22d3f93
refactor(api): restore override annotation for decode method
Oct 3, 2025
0bd966c
refactor(api): restore override annotation for decode method
Oct 3, 2025
024ec93
refactor(api): restore override annotation for decode method
Oct 3, 2025
ba98567
refactor(api): suppress resource warning for HttpClient instantiation
Oct 3, 2025
33543a3
refactor(api): restore override annotations and clean up method docum…
Oct 3, 2025
d0818b5
refactor(api): make relayName and relayUri final fields
Oct 3, 2025
74858ea
refactor: deleted files
Oct 3, 2025
03a405f
refactor(api): simplify tag addition logic in CalendarContent
Oct 3, 2025
5968fcc
refactor: update pull request template to clarify purpose section
Oct 3, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .github/pull_request_template.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
## Why now?
## Summary
<!-- Explain the problem, context, and why this change is needed. Link to the issue. -->

Related issue: #____

## What changed?
Expand Down
62 changes: 0 additions & 62 deletions .github/workflows/codex.yml

This file was deleted.

23 changes: 0 additions & 23 deletions .github/workflows/release-please.yml

This file was deleted.

1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -224,3 +224,4 @@ data

# Original versions of merged files
*.orig
/.qodana/
30 changes: 0 additions & 30 deletions PR_DRAFT.md

This file was deleted.

27 changes: 0 additions & 27 deletions PR_DRAFT_PR1.md

This file was deleted.

31 changes: 31 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,37 @@ See [`docs/CODEBASE_OVERVIEW.md`](docs/CODEBASE_OVERVIEW.md) for details about r
## Examples
Examples are located in the [`nostr-java-examples`](./nostr-java-examples) module.

- [`SpringSubscriptionExample`](nostr-java-examples/src/main/java/nostr/examples/SpringSubscriptionExample.java)
shows how to open a non-blocking `NostrSpringWebSocketClient` subscription and close it after a
fixed duration.

## Streaming subscriptions

The client and API layers expose a non-blocking streaming API for long-lived subscriptions. Use
`NostrSpringWebSocketClient.subscribe` to open a REQ subscription and receive relay messages via a
callback:

```java
Filters filters = new Filters(new KindFilter<>(Kind.TEXT_NOTE));
AutoCloseable subscription =
client.subscribe(
filters,
"example-subscription",
message -> {
// handle EVENT/NOTICE payloads on your own executor to avoid blocking the socket thread
},
error -> log.warn("Subscription error", error));

// ... keep the subscription open while processing events ...

subscription.close(); // sends CLOSE to the relay and releases the underlying WebSocket
```

Subscriptions must be closed by the caller to ensure a CLOSE frame is sent to the relay and to free
the dedicated WebSocket connection created for the REQ. Callbacks run on the WebSocket thread; for
high-throughput feeds, hand off work to a queue or executor to provide backpressure and keep the
socket responsive.

## Supported NIPs
The API currently implements the following [NIPs](https://github.com/nostr-protocol/nips):
- [NIP-1](https://github.com/nostr-protocol/nips/blob/master/01.md) - Basic protocol flow description
Expand Down
32 changes: 32 additions & 0 deletions docs/reference/nostr-java-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,14 @@ Abstraction over a WebSocket connection to a relay.
```java
<T extends BaseMessage> List<String> send(T eventMessage) throws IOException
List<String> send(String json) throws IOException
AutoCloseable subscribe(String requestJson,
Consumer<String> messageListener,
Consumer<Throwable> errorListener,
Runnable closeListener) throws IOException
<T extends BaseMessage> AutoCloseable subscribe(T eventMessage,
Consumer<String> messageListener,
Consumer<Throwable> errorListener,
Runnable closeListener) throws IOException
void close() throws IOException
```

Expand All @@ -75,6 +83,10 @@ Spring `TextWebSocketHandler` based implementation of `WebSocketClientIF`.
public StandardWebSocketClient(String relayUri)
public <T extends BaseMessage> List<String> send(T eventMessage) throws IOException
public List<String> send(String json) throws IOException
public AutoCloseable subscribe(String requestJson,
Consumer<String> messageListener,
Consumer<Throwable> errorListener,
Runnable closeListener) throws IOException
public void close() throws IOException
```

Expand All @@ -84,6 +96,14 @@ Wrapper that adds retry logic around a `WebSocketClientIF`.
```java
public List<String> send(BaseMessage eventMessage) throws IOException
public List<String> send(String json) throws IOException
public AutoCloseable subscribe(BaseMessage requestMessage,
Consumer<String> messageListener,
Consumer<Throwable> errorListener,
Runnable closeListener) throws IOException
public AutoCloseable subscribe(String json,
Consumer<String> messageListener,
Consumer<Throwable> errorListener,
Runnable closeListener) throws IOException
public List<String> recover(IOException ex, String json) throws IOException
public void close() throws IOException
```
Expand All @@ -95,12 +115,24 @@ High level client coordinating multiple relay connections and signing.
public NostrIF setRelays(Map<String,String> relays)
public List<String> sendEvent(IEvent event)
public List<String> sendRequest(List<Filters> filters, String subscriptionId)
public AutoCloseable subscribe(Filters filters, String subscriptionId, Consumer<String> listener)
public AutoCloseable subscribe(Filters filters,
String subscriptionId,
Consumer<String> listener,
Consumer<Throwable> errorListener)
public NostrIF sign(Identity identity, ISignable signable)
public boolean verify(GenericEvent event)
public Map<String,String> getRelays()
public void close()
```

`subscribe` opens a dedicated WebSocket per relay, returns immediately, and streams raw relay
messages to the provided listener. The returned `AutoCloseable` sends a `CLOSE` command and releases
resources when invoked. Because callbacks execute on the WebSocket thread, delegate heavy
processing to another executor to avoid stalling inbound traffic. The
[`SpringSubscriptionExample`](../../nostr-java-examples/src/main/java/nostr/examples/SpringSubscriptionExample.java)
demonstrates how to open a subscription and close it after a fixed duration.

### Configuration
- `RetryConfig` – enables Spring Retry support.
- `RelaysProperties` – maps relay names to URLs via configuration properties.
Expand Down
29 changes: 29 additions & 0 deletions nostr-java-api/src/main/java/nostr/api/NostrIF.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
import lombok.NonNull;
import nostr.base.IEvent;
import nostr.base.ISignable;
Expand Down Expand Up @@ -90,6 +91,34 @@ List<String> sendRequest(
@NonNull String subscriptionId,
Map<String, String> relays);

/**
* Subscribe to a stream of events for the given filter on configured relays.
*
* @param filters the filter describing events to stream
* @param subscriptionId identifier for the subscription
* @param listener consumer invoked for each raw relay message
* @return a handle that cancels the subscription when closed
*/
AutoCloseable subscribe(
@NonNull Filters filters,
@NonNull String subscriptionId,
@NonNull Consumer<String> listener);

/**
* Subscribe to a stream of events with custom error handling.
*
* @param filters the filter describing events to stream
* @param subscriptionId identifier for the subscription
* @param listener consumer invoked for each raw relay message
* @param errorListener optional consumer invoked when a transport error occurs
* @return a handle that cancels the subscription when closed
*/
AutoCloseable subscribe(
@NonNull Filters filters,
@NonNull String subscriptionId,
@NonNull Consumer<String> listener,
Consumer<Throwable> errorListener);

/**
* Sign a signable object with the provided identity.
*
Expand Down
Loading
Loading