Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
48 changes: 36 additions & 12 deletions .github/pull_request_template.md
Original file line number Diff line number Diff line change
@@ -1,21 +1,45 @@
## Summary
<!-- Explain the problem, context, and why this change is needed. Link to the issue. -->

Related issue: #____
<!-- Briefly explain the problem and why this change is needed. -->

Closes #____

## Type of change

<!-- Check all that apply -->

- [ ] `fix` - Bug fix (non-breaking)
- [ ] `feat` - New feature (non-breaking)
- [ ] `refactor` - Code change that neither fixes a bug nor adds a feature
- [ ] `docs` - Documentation only
- [ ] `test` - Adding or updating tests
- [ ] `perf` - Performance improvement
- [ ] `chore` - Build, CI, or tooling changes

## What changed?
<!-- Brief summary; suggest where to start reviewing if many files. -->

## BREAKING
<!-- If applicable, call it out explicitly. -->
<!-- ⚠️ BREAKING: Describe migration or impact. -->
<!-- Brief summary of changes. For large PRs, suggest where to start reviewing. -->

## Breaking changes

<!-- Delete this section if not applicable -->
<!-- ⚠️ BREAKING: Describe migration steps or impact on existing code. -->

## Testing

<!-- How was this tested? Include commands, test classes, or manual verification steps. -->

- [ ] Unit tests pass: `mvn test`
- [ ] Integration tests pass: `mvn verify` (requires Docker)

## Review focus
<!-- Ask for specific feedback, e.g., "Concurrency strategy OK?" or "API shape acceptable?" -->

<!-- Optional: Ask for specific feedback, e.g., "Is the error handling approach OK?" -->

## Checklist
- [ ] Scope ≤ 300 lines (or split/stack)
- [ ] Title is **verb + object** (e.g., “Refactor auth middleware to async”)
- [ ] Description links the issue and answers “why now?”
- [ ] **BREAKING** flagged if needed
- [ ] Tests/docs updated (if relevant)

- [ ] PR title follows conventional commits: `type(scope): description`
- [ ] Changes are focused and under 300 lines (or stacked PRs)
- [ ] Tests added/updated for new functionality
- [ ] No new compiler warnings introduced
- [ ] CHANGELOG.md updated (for user-facing changes)
260 changes: 260 additions & 0 deletions AGENTS.md

Large diffs are not rendered by default.

23 changes: 23 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,29 @@ The format is inspired by Keep a Changelog, and this project adheres to semantic

No unreleased changes yet.

## [1.2.1] - 2026-01-21

### Fixed
- 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.

### Changed
- 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<Kind> for safe, explicit handling of unknown kinds.

## [1.2.0] - 2025-12-26

### Fixed
- 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).

### Changed
- Switched integration tests to use strfry relay for improved robustness.

### Removed
- Removed AGENTS.md and CLAUDE.md documentation files from the repository.

## [1.1.1] - 2025-12-24

### Fixed
Expand Down
174 changes: 174 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## Project Overview

`nostr-java` is a Java SDK for the Nostr protocol. It provides utilities for creating, signing, and publishing Nostr events to relays. The project implements 20+ Nostr Implementation Possibilities (NIPs).

- **Language**: Java 21+
- **Build Tool**: Maven
- **Architecture**: Multi-module Maven project with 9 modules

## Module Architecture

The codebase follows a layered dependency structure. Understanding this hierarchy is essential for making changes:

1. **nostr-java-util** – Foundation utilities (no dependencies on other modules)
2. **nostr-java-crypto** – BIP340 Schnorr signatures (depends on util)
3. **nostr-java-base** – Common model classes (depends on crypto, util)
4. **nostr-java-event** – Event and tag definitions (depends on base, crypto, util)
5. **nostr-java-id** – Identity and key handling (depends on base, crypto)
6. **nostr-java-encryption** – Message encryption (depends on base, crypto, id)
7. **nostr-java-client** – WebSocket relay client (depends on event, base)
8. **nostr-java-api** – High-level API (depends on all above)
9. **nostr-java-examples** – Sample applications (depends on api)

**Key principle**: Lower-level modules cannot depend on higher-level ones. When adding features, place code at the lowest appropriate level.

## Common Development Commands

### Building and Testing

```bash
# Run all unit tests (no Docker required)
mvn clean test

# Run integration tests (requires Docker for Testcontainers)
mvn clean verify

# Run integration tests with verbose output
mvn -q verify

# Install artifacts without tests
mvn install -Dmaven.test.skip=true

# Run a specific test class
mvn -q test -Dtest=GenericEventBuilderTest

# Run a specific test method
mvn -q test -Dtest=GenericEventBuilderTest#testSpecificMethod
```

### Code Quality

```bash
# Verify code quality and run all checks
mvn -q verify

# Generate code coverage report (Jacoco)
mvn verify
# Reports: target/site/jacoco/index.html in each module
```

## Key Architectural Patterns

### Event System

- **GenericEvent** (`nostr-java-event/src/main/java/nostr/event/impl/GenericEvent.java`) is the core event class
- Events can be built using:
- Direct constructors with `PublicKey` and `Kind`/`Integer`
- Static `GenericEvent.builder()` for flexible construction
- All events must be signed before sending to relays
- Events support both NIP-defined kinds (via `Kind` enum) and custom kinds (via `Integer`)

### Client Architecture

Two WebSocket client implementations:

1. **StandardWebSocketClient** – Blocking, waits for relay responses with configurable timeout
2. **NostrSpringWebSocketClient** – Non-blocking with Spring WebSocket and retry support (3 retries, exponential backoff from 500ms)

Configuration properties:
- `nostr.websocket.await-timeout-ms=60000`
- `nostr.websocket.poll-interval-ms=500`

### Tag System

- Tags are represented by `BaseTag` and subclasses
- Custom tags can be registered via `TagRegistry`
- Serialization/deserialization handled by Jackson with custom serializers in `nostr.event.json.serializer`

### Identity and Signing

- `Identity` class manages key pairs
- Events implement `ISignable` interface
- Signing uses Schnorr signatures (BIP340)
- Public keys use Bech32 encoding (npub prefix)

## NIPs Implementation

The codebase implements NIPs through dedicated classes in `nostr-java-api`:
- NIP classes (e.g., `NIP01`, `NIP04`, `NIP25`) provide builder methods and utilities
- Event implementations in `nostr-java-event/src/main/java/nostr/event/impl/`
- Refer to `.github/copilot-instructions.md` for the full NIP specification links

When implementing new NIP support:
1. Add event class in `nostr-java-event` if needed
2. Create NIP helper class in `nostr-java-api`
3. Add tests in both modules
4. Update README.md with NIP reference
5. Add example in `nostr-java-examples`

## Testing Strategy

- **Unit tests** (`*Test.java`): No external dependencies, use mocks
- **Integration tests** (`*IT.java`): Use Testcontainers to start `nostr-rs-relay`
- Relay container image can be overridden in `src/test/resources/relay-container.properties`
- Integration tests may be retried once on failure (configured in failsafe plugin)

## Code Standards

- **Commit messages**: Must follow conventional commits format: `type(scope): description`
- Allowed types: `feat`, `fix`, `docs`, `style`, `refactor`, `perf`, `test`, `build`, `ci`, `chore`, `revert`
- See `commit_instructions.md` for full guidelines
- **PR target**: All PRs should target the `develop` branch
- **Code formatting**: Google Java Format (enforced by CI)
- **Test coverage**: Jacoco generates reports (enforced by CI)
- **Required**: All changes must include unit tests and documentation updates

## Dependency Management

- **BOM**: `nostr-java-bom` (version 1.1.1) manages all dependency versions
- Root `pom.xml` includes temporary module version overrides until next BOM release
- Never add version numbers to dependencies in child modules – let the BOM manage versions

## Documentation

Comprehensive documentation in `docs/`:
- `docs/GETTING_STARTED.md` – Installation and setup
- `docs/howto/use-nostr-java-api.md` – API usage guide
- `docs/howto/streaming-subscriptions.md` – Subscription management
- `docs/howto/custom-events.md` – Creating custom event types
- `docs/reference/nostr-java-api.md` – API reference
- `docs/CODEBASE_OVERVIEW.md` – Module layout and build instructions

## Common Patterns and Gotchas

### Event Building
```java
// Using builder for custom kinds
GenericEvent event = GenericEvent.builder()
.kind(customKindInteger)
.content("content")
.pubKey(publicKey)
.build();

// Using constructor for standard kinds
GenericEvent event = new GenericEvent(pubKey, Kind.TEXT_NOTE);
```

### Signing and Sending
```java
// Sign and send pattern
EventNostr nostr = new NIP01(identity);
nostr.createTextNote("Hello Nostr!")
.sign()
.send(relays);
```

### Custom Tags
Register custom tags in `TagRegistry` before deserializing events that contain them.

### WebSocket Sessions
Spring WebSocket client maintains persistent connections. Always close subscriptions properly to avoid resource leaks.
2 changes: 1 addition & 1 deletion nostr-java-api/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<parent>
<groupId>xyz.tcheeric</groupId>
<artifactId>nostr-java</artifactId>
<version>1.1.1</version>
<version>1.2.1</version>
<relativePath>../pom.xml</relativePath>
</parent>

Expand Down
75 changes: 75 additions & 0 deletions nostr-java-api/src/test/resources/strfry.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
##
## strfry config for integration testing (no whitelist)
##

db = "./strfry-db/"

dbParams {
maxreaders = 256
mapsize = 10995116277760
noReadAhead = false
}

events {
maxEventSize = 65536
rejectEventsNewerThanSeconds = 900
rejectEventsOlderThanSeconds = 94608000
rejectEphemeralEventsOlderThanSeconds = 60
ephemeralEventsLifetimeSeconds = 300
maxNumTags = 2000
maxTagValSize = 1024
}

relay {
bind = "0.0.0.0"
port = 7777
nofiles = 1000000
realIpHeader = ""

info {
name = "nostr-java test relay"
description = "strfry relay for nostr-java integration tests"
pubkey = ""
contact = ""
icon = ""
nips = ""
}

maxWebsocketPayloadSize = 131072
maxReqFilterSize = 200
autoPingSeconds = 55
enableTcpKeepalive = false
queryTimesliceBudgetMicroseconds = 10000
maxFilterLimit = 500
maxSubsPerConnection = 20

writePolicy {
# No write policy plugin - accept all events
plugin = ""
}

compression {
enabled = true
slidingWindow = true
}

logging {
dumpInAll = false
dumpInEvents = false
dumpInReqs = false
dbScanPerf = false
invalidEvents = true
}

numThreads {
ingester = 3
reqWorker = 3
reqMonitor = 3
negentropy = 2
}

negentropy {
enabled = true
maxSyncEvents = 1000000
}
}
2 changes: 1 addition & 1 deletion nostr-java-base/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<parent>
<groupId>xyz.tcheeric</groupId>
<artifactId>nostr-java</artifactId>
<version>1.1.1</version>
<version>1.2.1</version>
<relativePath>../pom.xml</relativePath>
</parent>

Expand Down
Loading
Loading