Skip to content

Conversation

@tcheeric
Copy link
Contributor

Description

This pull request introduces several updates and features, including:

  • End-to-end (e2e) integration tests for Bottin with domain and NIP-05 record management.
  • Implementation of the SPI pattern for pluggable NIP-05 and account managers.
  • Enhanced Bottin setup, including support for PostgreSQL and authentication.
  • Migration to JDK 21 for codebase and CI workflows.
  • Release version 0.1.0 and documentation updates for Bottin integration.

Related Issue

Fixes #

Type of Change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Documentation update
  • Refactoring (no functional changes)
  • Performance improvement
  • Test coverage improvement

Checklist

  • My code follows the project's code style
  • I have performed a self-review of my code
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation
  • My changes generate no new warnings
  • I have added tests that prove my fix is effective or that my feature works
  • New and existing unit tests pass locally with my changes
  • Any dependent changes have been merged and published

Testing

Test coverage includes both unit and comprehensive end-to-end tests for Bottin integration, verifying domain verification, NIP-05 record management, and well-known endpoint compliance. The updates have been verified using Testcontainers and Maven configurations for e2e profiles.

Screenshots (if applicable)

Additional Notes

This PR represents an essential milestone with initial version release (0.1.0) and significant groundwork for robust Bottin integration. Further enhancements and additional features may follow in subsequent releases.

tcheeric and others added 12 commits December 28, 2025 23:56
Implements Service Provider Interface (SPI) pattern to allow external
projects like bottin to provide persistent implementations of NIP-05
and account management.

Changes:
- Add Nip05ManagerProvider and AccountManagerProvider interfaces
- Add DefaultNip05ManagerProvider and DefaultAccountManagerProvider (priority 0)
- Enhance Nip05Manager with persistence methods (findByNip05, findByDomain, etc.)
- Enhance AccountManager with lookup methods (findAccount, exists, etc.)
- Add helper methods to Nip05Record (getRelays, getUsername, getDomain, fromBunkerKey)
- Add NIP-05 configuration properties to Spring Boot starter
- Update auto-configuration for provider selection by priority
- Add nsecbunker-account dependency to Spring Boot starter
- Create integration documentation for bottin

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <[email protected]>
Add detailed documentation explaining how nsecbunker-java leverages
bottin's NIP-05 functionalities through the SPI pattern:

- Auto-configuration flow diagram showing provider injection
- Provider selection logic with priority-based auto-selection
- NIP-05 operations table with method descriptions
- Transparent usage example demonstrating database-backed operations
- Troubleshooting section for verifying bottin integration is active
- Domain verification requirements for setupNip05()

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <[email protected]>
Add org.testcontainers:postgresql dependency to nsecbunker-e2e module
for Bottin e2e tests that require a PostgreSQL database.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <[email protected]>
Add comprehensive end-to-end tests for Bottin integration using
Testcontainers. Tests cover:

- Domain management (create, list, get, delete)
- NIP-05 record CRUD operations
- Well-known endpoint NIP-05 spec compliance
- Error handling (duplicate records, invalid pubkey)

Components added:
- BottinContainer: Testcontainer for docker.398ja.xyz/bottin image
- BottinTestClient: HTTP client for Bottin REST API
- BottinE2ETest: 18 test scenarios with ordered execution

Requires Bottin to support BOTTIN_VERIFICATION_SKIP=true for
auto-verifying domains in test mode.

Run with: mvn -Pe2e test -Dtest=BottinE2ETest

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <[email protected]>
Add Maven property to control Bottin domain verification skip mode:
- Default: true (skip verification in tests)
- Override with: -Dbottin.verification.skip=false

Property is passed to tests via Surefire systemPropertyVariables
and read by BottinContainer at initialization.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <[email protected]>
Update all module versions from 0.1.0-SNAPSHOT to 0.1.0
for the initial release.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <[email protected]>
Update BottinContainer to use docker.398ja.xyz/bottin-web:latest
instead of docker.398ja.xyz/bottin:latest.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <[email protected]>
Add required environment variables for Bottin to use PostgreSQL:
- SPRING_DATASOURCE_DRIVER_CLASS_NAME=org.postgresql.Driver
- SPRING_JPA_DATABASE_PLATFORM=org.hibernate.dialect.PostgreSQLDialect
- SPRING_FLYWAY_ENABLED=false (use Hibernate DDL auto instead)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <[email protected]>
Bottin's REST API requires authentication. Updated:

- BottinTestClient: Added constructor with username/password for
  Basic Auth, adds Authorization header to API requests
- BottinContainer: Added admin credentials configuration via
  BOTTIN_ADMIN_USERNAME and BOTTIN_ADMIN_PASSWORD env vars
- BottinE2ETest: Use authenticated client with admin credentials

Public endpoints (health, well-known) remain unauthenticated.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <[email protected]>
- Enable Flyway migrations (SPRING_FLYWAY_ENABLED=true) instead of
  Hibernate DDL auto to properly create database schema
- Add forceVerifyDomain() helper that directly updates PostgreSQL
  to mark domains as verified (bypasses normal verification flow)
- Update shouldRegisterNewDomain test to force-verify domain after
  creation, enabling NIP-05 record tests to proceed

Bottin requires verified domains before creating NIP-05 records.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <[email protected]>
Add @tag("bottin") to BottinE2ETest and create e2e-bottin profile
that filters tests by this tag.

Run Bottin tests only:
  mvn -Pe2e-bottin test -pl nsecbunker-tests/nsecbunker-e2e

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <[email protected]>
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This pull request introduces Bottin integration for persistent NIP-05 identity management, implements a Service Provider Interface (SPI) pattern for pluggable account and NIP-05 managers, migrates the codebase from JDK 17 to JDK 21, and releases version 0.1.0. The changes establish a foundation for extensible identity management while maintaining backward compatibility through sensible defaults.

Key changes:

  • Implementation of SPI pattern with AccountManagerProvider and Nip05ManagerProvider interfaces allowing pluggable storage backends
  • Comprehensive e2e test suite for Bottin integration using Testcontainers with PostgreSQL
  • Enhanced domain models (Nip05Record, AccountManager, Nip05Manager) with additional query and management methods
  • Spring Boot auto-configuration enhancements for provider selection based on priority
  • Version bump to 0.1.0 across all Maven modules

Reviewed changes

Copilot reviewed 34 out of 34 changed files in this pull request and generated 10 comments.

Show a summary per file
File Description
pom.xml (multiple) Version update from 0.1.0-SNAPSHOT to 0.1.0 release across all modules
BottinContainer.java New Testcontainer implementation for Bottin service with PostgreSQL configuration
BottinTestClient.java HTTP client for e2e tests providing domain and NIP-05 record management operations
BottinE2ETest.java Comprehensive e2e test suite verifying domain management, NIP-05 CRUD operations, and well-known endpoint compliance
AccountManagerProvider.java New SPI for pluggable account storage backends with priority-based selection
Nip05ManagerProvider.java New SPI for pluggable NIP-05 storage backends with priority-based selection
DefaultAccountManagerProvider.java Default in-memory implementation of AccountManagerProvider
DefaultNip05ManagerProvider.java Default in-memory implementation of Nip05ManagerProvider
Nip05Record.java Enhanced with helper methods for relay parsing, username/domain extraction, and factory methods
AccountManager.java Extended interface with query methods (findAccount, findByUsername, findByDomain, exists, deleteAccount, count)
Nip05Manager.java Extended interface with query methods (findByNip05, findByDomain, findByPubkey, deleteNip05, updateRelays, count)
NsecBunkerAutoConfiguration.java Added bean creation for AccountManager and Nip05Manager with provider selection logic
NsecBunkerProperties.java Added Nip05 nested configuration class for NIP-05 settings
bottin.md New comprehensive integration guide for Bottin with usage examples and troubleshooting
README.md Updated CI badge URL and JDK version from 17 to 21

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 61 to 72
/**
* Finds an account by username.
*
* <p>Default implementation returns empty. Persistent implementations
* should override this method to provide username-based lookup.</p>
*
* @param username username to search for
* @return the account if found, empty otherwise
*/
default CompletableFuture<Optional<AccountRegistrationResult>> findByUsername(String username) {
return CompletableFuture.completedFuture(Optional.empty());
}
Copy link

Copilot AI Dec 31, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The default implementation of findByUsername searches by username alone, but usernames are not unique across domains. A user "alice" on "example.com" and "alice" on "another.com" are different accounts. This method could return ambiguous results when the same username exists on multiple domains. Consider either requiring both username and domain parameters, or documenting that this returns the first match and may be ambiguous in multi-domain scenarios.

Copilot uses AI. Check for mistakes.
Comment on lines +409 to +410
// Assert
assertThat(response.statusCode()).isEqualTo(200);
Copy link

Copilot AI Dec 31, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The toggle logic in this test is problematic. The comment says "ensure record is enabled" but then toggles without checking the current state. This could leave the record in either state (enabled or disabled) depending on what happened in previous tests. This will cause test flakiness.

Instead, should verify the current state first and only toggle if needed to ensure the record is enabled before testing the well-known endpoint.

Copilot uses AI. Check for mistakes.
Comment on lines 64 to 68
try {
return DEFAULT_MAPPER.readValue(relaysJson, STRING_LIST_TYPE);
} catch (JsonProcessingException e) {
return List.of();
}
Copy link

Copilot AI Dec 31, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The getRelays method silently swallows JsonProcessingException and returns an empty list. This could hide data corruption or invalid JSON in the relaysJson field. Consider logging the exception at least at debug level to aid troubleshooting, especially since this is returning parsed data that callers may rely on.

Copilot uses AI. Check for mistakes.
# nsecbunker-java

[![CI](https://github.com/tcheeric/nsecbunker-java/actions/workflows/ci.yml/badge.svg)](https://github.com/tcheeric/nsecbunker-java/actions/workflows/ci.yml)
[![CI](https://github.com/398ja/nsecbunker-java/actions/workflows/ci.yml/badge.svg)](https://github.com/398ja/nsecbunker-java/actions/workflows/ci.yml)
Copy link

Copilot AI Dec 31, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The GitHub repository URL in the CI badge has changed from 'tcheeric' to '398ja'. Ensure this repository actually exists at the new URL and that the CI workflow is properly configured there. If this is a typo or temporary URL, it should be corrected before release.

Suggested change
[![CI](https://github.com/398ja/nsecbunker-java/actions/workflows/ci.yml/badge.svg)](https://github.com/398ja/nsecbunker-java/actions/workflows/ci.yml)
[![CI](https://github.com/tcheeric/nsecbunker-java/actions/workflows/ci.yml/badge.svg)](https://github.com/tcheeric/nsecbunker-java/actions/workflows/ci.yml)

Copilot uses AI. Check for mistakes.
Comment on lines +77 to +96
public String getUsername() {
if (nip05 == null || !nip05.contains("@")) {
return null;
}
return nip05.split("@")[0];
}

/**
* Extracts the domain portion of the NIP-05 identifier.
*
* @return the domain (e.g., "example.com" from "[email protected]")
*/
@JsonIgnore
public String getDomain() {
if (nip05 == null || !nip05.contains("@")) {
return null;
}
String[] parts = nip05.split("@");
return parts.length > 1 ? parts[1] : null;
}
Copy link

Copilot AI Dec 31, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The getUsername and getDomain methods use String.split("@") without proper handling of multiple @ symbols. If nip05 contains multiple @ characters (e.g., "alice@[email protected]"), this will incorrectly parse the username and domain. Consider using indexOf and substring for more robust parsing, or validating the NIP-05 format to ensure it contains exactly one @ symbol.

Copilot uses AI. Check for mistakes.
Comment on lines 32 to 33
public DefaultNip05ManagerProvider() {
this(new DefaultAccountManager(), new ObjectMapper());
Copy link

Copilot AI Dec 31, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The no-arg constructor creates a new DefaultAccountManager instance which is then potentially replaced in the full constructor (line 52). This means two AccountManager instances may be created unnecessarily. Consider having the no-arg constructor delegate directly to the full constructor with null values, letting the null check happen once.

Copilot uses AI. Check for mistakes.
@github-actions
Copy link

github-actions bot commented Dec 31, 2025

Qodana for JVM

23 new problems were found

Inspection name Severity Problems
Vulnerable declared dependency 🔶 Warning 3
Mismatched query and update of collection 🔶 Warning 1
Vulnerable declared dependency ◽️ Notice 19

☁️ View the detailed Qodana report

Detected 74 dependencies

Third-party software list

This page lists the third-party software dependencies used in project

Dependency Version Licenses
annotations 13.0 Apache-2.0
aspectjweaver 1.9.25.1 BSD-3-Clause
bcprov-jdk18on 1.81 MIT
byte-buddy 1.17.8 Apache-2.0
commons-lang3 3.17.0 Apache-2.0
commons-math3 3.6.1 Apache-2.0
commons-text 1.12.0 Apache-2.0
hdrhistogram 2.2.2 BSD-2-Clause
jackson-annotations 2.17.0 Apache-2.0
jackson-core 2.17.0 Apache-2.0
jackson-databind 2.17.0 Apache-2.0
jackson-datatype-jdk8 2.17.0 Apache-2.0
jackson-datatype-jsr310 2.17.0 Apache-2.0
jackson-module-blackbird 2.17.0 BSD-3-CLAUSE-NO-TRADEMARK
jackson-module-parameter-names 2.17.0 Apache-2.0
jakarta.annotation-api 2.1.1 Classpath-exception-2.0
EPL-2.0
GPL-2.0-only
jmh-core 1.37 GPL-2.0-only
ORACLE-OPENJDK-EXCEPTION-2.0
jmh-generator-annprocess 1.37 GPL-2.0-only
ORACLE-OPENJDK-EXCEPTION-2.0
jopt-simple 5.0.4 MIT
jul-to-slf4j 2.0.17 MIT
kotlin-stdlib-common 1.9.25 Apache-2.0
kotlin-stdlib-jdk7 1.9.25 Apache-2.0
kotlin-stdlib-jdk8 1.9.25 Apache-2.0
kotlin-stdlib 1.9.25 Apache-2.0
latencyutils 2.0.3 CC0-1.0
log4j-api 2.24.3 Apache-2.0
log4j-to-slf4j 2.24.3 Apache-2.0
logback-classic 1.5.15 EPL-1.0
LGPL-2.0-or-later
logback-core 1.5.15 EPL-1.0
LGPL-2.0-or-later
lombok 1.18.32 BSD-3-CLAUSE-NO-TRADEMARK
MIT
micrometer-commons 1.14.3 Apache-2.0
micrometer-core 1.14.3 Apache-2.0
micrometer-jakarta9 1.14.3 Apache-2.0
micrometer-observation 1.14.3 Apache-2.0
nostr-java-api 1.2.0 MIT
nostr-java-base 1.2.0 MIT
nostr-java-client 1.2.0 MIT
nostr-java-crypto 1.2.0 MIT
nostr-java-encryption 1.2.0 MIT
nostr-java-event 1.2.0 MIT
nostr-java-id 1.2.0 MIT
nostr-java-util 1.2.0 MIT
okhttp 4.12.0 Apache-2.0
okio-jvm 3.6.0 Apache-2.0
okio 3.6.0 Apache-2.0
slf4j-api 2.0.12 MIT
snakeyaml 2.4 Apache-2.0
spring-aop 6.2.15 Apache-2.0
spring-aspects 6.2.15 Apache-2.0
spring-beans 6.2.15 Apache-2.0
spring-boot-actuator-autoconfigure 3.5.9 Apache-2.0
spring-boot-actuator 3.5.9 Apache-2.0
spring-boot-autoconfigure 3.5.9 Apache-2.0
spring-boot-configuration-processor 3.5.9 Apache-2.0
spring-boot-starter-actuator 3.5.9 Apache-2.0
spring-boot-starter-json 3.5.9 Apache-2.0
spring-boot-starter-logging 3.5.9 Apache-2.0
spring-boot-starter-tomcat 3.5.9 Apache-2.0
spring-boot-starter-web 3.5.9 Apache-2.0
spring-boot-starter-websocket 3.5.9 Apache-2.0
spring-boot-starter 3.5.9 Apache-2.0
spring-boot 3.5.9 Apache-2.0
spring-context 6.2.15 Apache-2.0
spring-core 6.2.15 Apache-2.0
spring-expression 6.2.15 Apache-2.0
spring-jcl 6.2.15 Apache-2.0
spring-messaging 6.2.15 Apache-2.0
spring-retry 2.0.12 Apache-2.0
spring-web 6.2.15 Apache-2.0
spring-webmvc 6.2.15 Apache-2.0
spring-websocket 6.2.15 Apache-2.0
tomcat-embed-core 10.1.50 Apache-2.0
CDDL-1.0
PROPRIETARY-LICENSE
tomcat-embed-el 10.1.50 Apache-2.0
tomcat-embed-websocket 10.1.50 Apache-2.0
Contact Qodana team

Contact us at [email protected]

Code quality fixes:
- Make RelayConnection and RelayPool final (CT_CONSTRUCTOR_THROW)
- Make AdminConnectionListener static inner class
- Fix similar log messages in RelayPool, alerting classes
- Fix MismatchedCollectionQueryUpdate in WebhookAlertDelivery, RelayPool
- Add @serial annotations to exception classes (MissingSerialAnnotation)
- Remove dead local store in RelayHealthMonitor (DLS_DEAD_LOCAL_STORE)
- Fix SQL injection pattern warning in BottinE2ETest
- Fix duplicated JSON serialization in Nip05Record
- Remove unused imports and code across modules

Dependency updates for CVE fixes:
- Spring Boot 3.2.4 -> 3.5.9
- JUnit 5.10.2 -> 5.12.2
- Logback 1.5.3 -> 1.5.15
- Micrometer 1.12.4 -> 1.14.3
- maven-pmd-plugin 3.21.2 -> 3.26.0 (Java 21 support)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <[email protected]>
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 107 out of 107 changed files in this pull request and generated no new comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@tcheeric tcheeric merged commit f9c639d into master Dec 31, 2025
15 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants