Navigation: Docs index · Getting started · API how-to · Multi-relay publishing · Streaming subscriptions · Custom events
This document provides an overview of the public API exposed by the nostr-java modules. It lists the major classes, their key method signatures, and shows brief usage examples.
Represents a Nostr identity backed by a private key. Derives the public key and signs ISignable objects.
public static Identity create(PrivateKey privateKey)
public static Identity create(String privateKey)
public static Identity generateRandomIdentity()
public PublicKey getPublicKey()
public Signature sign(ISignable signable)Usage:
Identity identity = Identity.generateRandomIdentity();
PublicKey pub = identity.getPublicKey();
identity.sign(event);The sole event class for all Nostr event kinds. Implements ISignable.
// Builder
public static GenericEventBuilder builder()
// Fields
public String getId()
public PublicKey getPubKey()
public Long getCreatedAt()
public int getKind()
public List<GenericTag> getTags()
public String getContent()
public Signature getSignature()
// Kind classification
public boolean isReplaceable()
public boolean isEphemeral()
public boolean isAddressable()
// Bech32 encoding
public String toBech32()Usage:
GenericEvent event = GenericEvent.builder()
.pubKey(identity.getPublicKey())
.kind(Kinds.TEXT_NOTE)
.content("Hello Nostr!")
.tags(List.of(GenericTag.of("t", "nostr")))
.build();
identity.sign(event);The sole tag class. A code and a list of string parameters.
// Factory methods
public static GenericTag of(String code, String... params)
public static GenericTag of(String code, List<String> params)
// Accessors
public String getCode()
public List<String> getParams()
public List<String> toArray()Usage:
GenericTag tag = GenericTag.of("e", "eventId123", "wss://relay.example.com", "reply");
tag.getCode() // "e"
tag.getParams().get(0) // "eventId123"
tag.toArray() // ["e", "eventId123", "wss://relay.example.com", "reply"]Static int constants for common event kinds plus range-check utilities.
public static final int SET_METADATA = 0;
public static final int TEXT_NOTE = 1;
public static final int RECOMMEND_SERVER = 2;
public static final int CONTACT_LIST = 3;
public static final int ENCRYPTED_DIRECT_MESSAGE = 4;
public static final int DELETION = 5;
public static final int REPOST = 6;
public static final int REACTION = 7;
public static final int ZAP_REQUEST = 9734;
public static final int ZAP_RECEIPT = 9735;
public static boolean isValid(int kind)
public static boolean isReplaceable(int kind)
public static boolean isEphemeral(int kind)
public static boolean isAddressable(int kind)Builder-based composable filter for relay REQ messages.
public static EventFilterBuilder builder()
// Builder methods
.kinds(List<Integer> kinds)
.authors(List<String> authors)
.ids(List<String> ids)
.since(long timestamp)
.until(long timestamp)
.limit(int limit)
.addTagFilter(String tagCode, List<String> values)
.build()Usage:
EventFilter filter = EventFilter.builder()
.kinds(List.of(Kinds.TEXT_NOTE))
.authors(List.of(pubKeyHex))
.since(timestamp)
.limit(100)
.build();Container for one or more EventFilter instances (OR logic for REQ messages).
public Filters(EventFilter... filters)
public Filters(Filterable... filterables)| Class | Command | Purpose |
|---|---|---|
EventMessage |
EVENT |
Send or receive an event |
ReqMessage |
REQ |
Subscribe to events matching filters |
CloseMessage |
CLOSE |
Close a subscription |
OkMessage |
OK |
Relay acknowledgment |
EoseMessage |
EOSE |
End of stored events |
NoticeMessage |
NOTICE |
Relay notice/error |
// Encode a message
String json = new EventMessage(event).encode();
// Decode a message. The decoder is generic: name the expected message type when you know it,
// so the result needs no cast.
BaseMessage message = new BaseMessageDecoder<>().decode(json);
EventMessage event = new BaseMessageDecoder<EventMessage>().decode(json);Spring TextWebSocketHandler-based WebSocket client with retry and Virtual Thread support.
Constructors:
public NostrRelayClient(String relayUri)
public NostrRelayClient(String relayUri, long awaitTimeoutMs)Blocking operations:
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 <T extends BaseMessage> AutoCloseable subscribe(T message,
Consumer<String> messageListener,
Consumer<Throwable> errorListener,
Runnable closeListener) throws IOException
public void close() throws IOExceptionAsync operations (Virtual Threads):
public static CompletableFuture<NostrRelayClient> connectAsync(String relayUri)
public static CompletableFuture<NostrRelayClient> connectAsync(String relayUri, long awaitTimeoutMs)
public CompletableFuture<List<String>> sendAsync(String json)
public <T extends BaseMessage> CompletableFuture<List<String>> sendAsync(T eventMessage)
public CompletableFuture<AutoCloseable> subscribeAsync(String requestJson,
Consumer<String> messageListener,
Consumer<Throwable> errorListener,
Runnable closeListener)Usage:
// Blocking
try (NostrRelayClient client = new NostrRelayClient("wss://relay.example.com")) {
List<String> responses = client.send(new EventMessage(event));
}
// Async
NostrRelayClient.connectAsync("wss://relay.example.com")
.thenCompose(client -> client.sendAsync(new EventMessage(event)))
.thenAccept(responses -> System.out.println("Done: " + responses));Thrown when the relay does not respond within the configured timeout. Extends IOException.
public String getRelayUri()
public long getTimeoutMs()Enum tracking WebSocket connection state.
CONNECTING, CONNECTED, RECONNECTING, CLOSED| Property | Default | Description |
|---|---|---|
nostr.websocket.await-timeout-ms |
60000 |
Max time to await a relay response |
nostr.websocket.max-idle-timeout-ms |
3600000 |
Max idle timeout for WebSocket sessions |
nostr.websocket.max-text-message-buffer-size |
1048576 |
WebSocket text message buffer size |
nostr.websocket.max-binary-message-buffer-size |
1048576 |
WebSocket binary message buffer size |
Send and subscribe operations are annotated with @NostrRetryable:
- Included exception:
IOException - Max attempts:
3 - Backoff: initial
500ms, multiplier2.0
The entry point for applications. See the multi-relay how-to for worked examples.
An identity, a set of relays, and the operations between them.
static NostrClient.Builder builder()
PublishResult publish(GenericEvent event) throws NoRelayAcceptedException
PublishResult publishAs(Identity signer, GenericEvent event) throws NoRelayAcceptedException
PublishResult publishTextNote(String content) throws NoRelayAcceptedException
RelaySubscription subscribe(List<EventFilter> filters, SubscriptionListener listener)
RecipientDeliveryOutcome sendDirectMessage(PublicKey recipient, String content)
List<RecipientDeliveryOutcome> sendDirectMessage(List<PublicKey> recipients, String content)
ChatMessage readDirectMessage(GenericEvent giftWrap)
Optional<DirectMessageRelayList> findDirectMessageRelays(PublicKey owner)
RelayPool getRelayPool()
Identity getIdentity()
void close()Builder: identity(Identity), relays(String...), relays(List<String>),
relayPool(RelayPool), connectionFactory(RelayConnectionFactory), build().
Ownership follows construction. A pool built from relay URIs is closed with the client; a
pool passed to relayPool(...) is left to whoever created it, so it may outlive the client.
Whether one participant received a direct message: recipient(), status()
(DELIVERED, UNREACHABLE, REJECTED), relays(), isDelivered(), findReason().
A recipient who published no kind-10050 relay list is UNREACHABLE, since NIP-17 treats that as
declining private messages.
Implements DirectMessageRelayLookup by querying relays for kind-10050 lists.
A mutable set of relay connections, with fan-out publishing and fan-in subscriptions.
RelayPool(List<String> relayUris, RelayConnectionFactory connectionFactory)
RelayPool(List<String> relayUris, RelayConnectionFactory factory, Duration publishTimeout)
// further constructors add reconnectInterval and backlogTimeout
PublishResult publish(GenericEvent event) throws NoRelayAcceptedException
RelaySubscription subscribe(List<EventFilter> filters, SubscriptionListener listener)
RelaySubscription subscribe(List<EventFilter> filters, SubscriptionListener listener, int windowSize)
boolean addRelay(String relayUri)
boolean releaseRelay(String relayUri)
boolean removeRelay(String relayUri)
List<String> retryUnreachableRelays()
List<String> getRelays()
List<String> getConnectedRelays()
List<String> getUnreachableRelays()
Optional<ConnectionState> getConnectionState(String relayUri)
void close()Defaults: DEFAULT_PUBLISH_TIMEOUT 10s, DEFAULT_RECONNECT_INTERVAL 30s,
DEFAULT_BACKLOG_TIMEOUT 10s.
What every relay did with one event: getEventId(), getOutcomes(), findOutcome(String),
getAcceptingRelays(), getFailures(), isAccepted(), isAcceptedByAllRelays().
One relay's verdict: relayUri(), status() (ACCEPTED, REJECTED, TIMED_OUT,
UNREACHABLE), reason(), isAccepted(), findReason().
Thrown when no relay stored the event. Carries the full PublishResult via
getPublishResult(), so the caller can still see what each relay said.
One subscription across many relays: getSubscriptionId(), getSubscribedRelays(),
hasAnnouncedEndOfStoredEvents(), close().
onEvent(GenericEvent), plus optional onEndOfStoredEvents() and
onRelayFailure(String, Throwable).
Events are delivered in the order the relay sent them, so stored events always arrive before the end-of-backlog signal.
The seam between relay coordination and transport. NostrRelayClient implements
RelayConnection; supplying a different factory is how tests substitute scripted relays.
One request in flight per relay. NostrRelayClient serves a single request at a time, so
the pool serialises operations per relay. Throughput to any one relay is bounded by round-trip
latency. Fan-out across relays is unaffected. Making the client multiplex is separate work; see
ADR-0004.
Transient relays are briefly shared. A relay added to deliver a direct message is available to other operations while it remains connected. This is benign, but it means the pool can publish through a relay the caller never configured.
De-duplication is windowed. Events evicted from the bounded window are treated as new if they arrive again, so a copy arriving much later than its siblings can be delivered twice. The default window is sized well beyond realistic cross-relay spread.
Strategy interface for message encryption.
String encrypt(String message)
String decrypt(String message)Implementations:
MessageCipher04— NIP-04 direct message encryption (legacy).MessageCipher44— NIP-44 versioned encryption (recommended).
BIP-340 Schnorr signature utility.
static byte[] sign(byte[] msg, byte[] secKey, byte[] auxRand)
static boolean verify(byte[] msg, byte[] pubKey, byte[] sig)
static byte[] generatePrivateKey()
static byte[] genPubKey(byte[] secKey)Bech32/Bech32m encoding for NIP-19.
static String toBech32(Bech32Prefix hrp, byte[] hexKey)
static String fromBech32(String str)General helper functions using java.util.HexFormat.
static String bytesToHex(byte[] bytes)
static byte[] hexToBytes(String hex)
static byte[] sha256(byte[] data)
static byte[] createRandomByteArray(int len)Nostr public key value object with Bech32 encoding (npub prefix).
public PublicKey(String hex)
public String toBech32()
public String toString() // hex representationNostr private key value object with Bech32 encoding (nsec prefix).
public PrivateKey(String hex)
public String toBech32()BIP-340 Schnorr signature value object.
public Signature(String hex)This reference is a starting point; consult the source for complete details.