Thank you for contributing to nostr-java! This project implements the Nostr protocol. For a complete index of current Nostr Implementation Possibilities (NIPs), see AGENTS.md.
- Getting Started
- Development Guidelines
- Coding Standards
- Architecture Guidelines
- Adding New NIPs
- Testing Requirements
- Commit Guidelines
- Pull Request Guidelines
- Java 21+ - Required for building and running the project
- Maven 3.8+ - For dependency management and building
- Git - For version control
- Fork the repository on GitHub
- Clone your fork:
git clone https://github.com/YOUR_USERNAME/nostr-java.git - Add upstream remote:
git remote add upstream https://github.com/tcheeric/nostr-java.git - Build:
mvn clean install - Run tests:
mvn test
- All changes must include unit tests and update relevant documentation.
- Use clear, descriptive names and remove unused imports.
- Prefer readable, maintainable code over clever shortcuts.
- Run
mvn -q verifyfrom the repository root before committing. - Submit pull requests against the
mainbranch.
✅ All tests pass: mvn test
✅ Code compiles: mvn clean install
✅ JavaDoc complete for public APIs
✅ Branch up-to-date with latest main
This project follows Clean Code principles. Key guidelines:
- Single Responsibility Principle - Each class should have one reason to change
- DRY (Don't Repeat Yourself) - Avoid code duplication
- Meaningful Names - Use descriptive, intention-revealing names
- Small Functions - Functions should do one thing well
Classes:
- Entities: Noun names (e.g.,
GenericEvent,UserProfile) - Builders: End with
Builder(e.g.,NIP01EventBuilder) - Factories: End with
Factory(e.g.,NIP01TagFactory) - Validators: End with
Validator(e.g.,EventValidator) - Serializers: End with
Serializer(e.g.,EventSerializer) - NIP implementations: Use
NIPxxformat (e.g.,NIP01,NIP57)
Methods:
- Getters:
getKind(),getPubKey() - Setters:
setContent(),setTags() - Booleans:
isEphemeral(),hasTag() - Factory methods:
createEventTag(),buildTextNote()
Variables:
- Use camelCase (e.g.,
eventId,publicKey) - Constants: UPPER_SNAKE_CASE (e.g.,
REPLACEABLE_KIND_MIN)
- Indentation: 2 spaces (no tabs)
- Line length: Max 100 characters (soft limit)
- Use Lombok:
@Data,@Builder,@NonNull,@Slf4j - Remove unused imports
This project follows Clean Architecture. See docs/explanation/architecture.md for details.
nostr-java/
├── nostr-java-base/ # Domain entities
├── nostr-java-crypto/ # Cryptography
├── nostr-java-event/ # Event implementations
├── nostr-java-api/ # NIP facades
├── nostr-java-client/ # Relay clients
- Facade: NIP implementation classes (e.g., NIP01, NIP57)
- Builder: Complex object construction
- Factory: Creating instances (tags, messages)
- Template Method: Validation with overrideable steps
- Utility: Stateless helper classes
- Read the NIP spec at https://github.com/nostr-protocol/nips
- Create event class (if needed) in
nostr-java-event - Create facade in
nostr-java-api - Write tests (minimum 80% coverage)
- Add JavaDoc with usage examples
- Update README NIP compliance matrix
/**
* Facade for NIP-XX (Feature Name).
*
* <p><b>Usage Example:</b>
* <pre>{@code
* NIPxx nip = new NIPxx(identity);
* nip.createEvent("content")
* .sign()
* .send(relayUri);
* }</pre>
*
* @see <a href="https://github.com/nostr-protocol/nips/blob/master/XX.md">NIP-XX</a>
* @since 0.x.0
*/
public class NIPxx extends EventNostr {
// Implementation
}See docs/explanation/architecture.md for detailed step-by-step guide.
- Minimum coverage: 80% for new code
- Test all edge cases: null values, empty strings, invalid inputs
- Use descriptive test names or
@DisplayName
- See
nostr-java-api/src/test/java/nostr/api/client/README.mdfor structure and naming. - Naming conventions:
NostrSpringWebSocketClient*for high‑level client behaviorWebSocketHandler*for internal handler semantics (send/close/request)NostrRequestDispatcher*andNostrSubscriptionManager*for dispatcher/manager lifecycles
- Use
nostr.api.TestHandlerFactoryto constructWebSocketClientHandlerfrom tests outsidenostr.api.
- See
nostr-java-client/src/test/java/nostr/client/springwebsocket/README.mdfor an overview of the Spring WebSocket client test suite (retry/subscribe/timeout behavior).
@Test
@DisplayName("Validator should reject negative kind values")
void testValidateKindRejectsNegative() {
Integer invalidKind = -1;
AssertionError error = assertThrows(
AssertionError.class,
() -> EventValidator.validateKind(invalidKind)
);
assertTrue(error.getMessage().contains("non-negative"));
}- All commit messages must follow the requirements in
commit_instructions.md. - PR titles and commit messages must use the
type(scope): descriptionformat and allowed types. - See the commit instructions file for details and examples.
feat, fix, docs, style, refactor, perf, test, build, ci, chore, revert
feat(auth): add magic-link loginfix(api): handle 429 with exponential backoffdocs(readme): clarify local setuprefactor(search): extract ranking pipeline
- In the PR body, add:
Closes #123(orFixes ABC-456for Jira). GitHub will auto-close on merge.
- Summaries in pull requests must cite file paths and include testing output.
- Open pull requests using the template at
.github/pull_request_template.mdand complete every section.
By following these conventions, contributors help keep the codebase maintainable and aligned with the Nostr specifications.