From 7cc5d37cbf4f02f0bb7eee6342dc1ede5a841a7b Mon Sep 17 00:00:00 2001 From: DanConwayDev Date: Fri, 9 Jan 2026 07:57:54 +0000 Subject: feat: replace owner-npub with relay-owner-nsec for persistent operator identity Replace the owner-npub configuration option with relay-owner-nsec to provide a persistent cryptographic identity for the relay operator. This addresses NIP-42 authentication requirements discovered during sync debugging. Motivation: - Some relays (e.g., relay.damus.io) require NIP-42 authentication for advanced features like NIP-77 negentropy sync - Previously used random ephemeral keys per connection, providing no persistent identity - Other relays can now recognize us by pubkey for reputation-based rate limiting - Ensures consistency between NIP-11 pubkey and authentication key Changes: - Config: relay_owner_nsec with auto-load/generate from .relay-owner.nsec - NIP-11: Pubkey derived from nsec instead of separate npub field - Sync: RelayConnection now uses operator keys for NIP-42 auth - Docs: Updated README, .env.example, and added .relay-owner.nsec to gitignore Key Features: - Auto-generates key on first run and saves to .relay-owner.nsec - Loads existing key from file on subsequent runs - Can override via CLI flag or environment variable - Enables reputation building across relay network - Future-ready for event signing and WoT calculations Testing: - 225/232 tests passing (7 pre-existing purgatory failures unrelated) - Verified key generation, loading, and NIP-11 derivation - Release build successful Related: work/sync-debug-analysis.md, work/relay-owner-nsec-implementation.md --- src/sync/relay_connection.rs | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) (limited to 'src/sync/relay_connection.rs') diff --git a/src/sync/relay_connection.rs b/src/sync/relay_connection.rs index 21d864d..d0090c8 100644 --- a/src/sync/relay_connection.rs +++ b/src/sync/relay_connection.rs @@ -106,9 +106,10 @@ impl RelayConnection { /// /// # Arguments /// * `url` - The relay URL to connect to (with or without scheme, e.g., "relay.example.com" or "wss://relay.example.com") - pub fn new(url: String) -> Self { + /// * `keys` - Cryptographic keys for NIP-42 authentication (typically the relay operator's keys) + pub fn new(url: String, keys: Keys) -> Self { let normalized_url = Self::normalize_url(&url); - let client = Client::default(); + let client = Client::new(keys); Self { url: normalized_url, client, @@ -122,9 +123,10 @@ impl RelayConnection { /// # Arguments /// * `url` - The relay URL to connect to (with or without scheme, e.g., "relay.example.com" or "wss://relay.example.com") /// * `database` - Shared database for local event comparison during negentropy sync - pub fn new_with_database(url: String, database: SharedDatabase) -> Self { + /// * `keys` - Cryptographic keys for NIP-42 authentication (typically the relay operator's keys) + pub fn new_with_database(url: String, database: SharedDatabase, keys: Keys) -> Self { let normalized_url = Self::normalize_url(&url); - let client = Client::default(); + let client = Client::new(keys); Self { url: normalized_url, client, @@ -553,19 +555,22 @@ mod tests { #[test] fn test_new_normalizes_url() { - let conn = RelayConnection::new("relay.example.com".to_string()); + let keys = Keys::generate(); + let conn = RelayConnection::new("relay.example.com".to_string(), keys); assert_eq!(conn.url(), "wss://relay.example.com"); } #[test] fn test_new_preserves_wss_scheme() { - let conn = RelayConnection::new("wss://relay.example.com".to_string()); + let keys = Keys::generate(); + let conn = RelayConnection::new("wss://relay.example.com".to_string(), keys); assert_eq!(conn.url(), "wss://relay.example.com"); } #[test] fn test_new_preserves_ws_scheme() { - let conn = RelayConnection::new("ws://relay.example.com".to_string()); + let keys = Keys::generate(); + let conn = RelayConnection::new("ws://relay.example.com".to_string(), keys); assert_eq!(conn.url(), "ws://relay.example.com"); } @@ -573,7 +578,8 @@ mod tests { fn test_new_with_database_normalizes_url() { // This test just verifies the URL normalization works // We can't easily test with_database without a real database - let conn = RelayConnection::new("git.shakespeare.diy".to_string()); + let keys = Keys::generate(); + let conn = RelayConnection::new("git.shakespeare.diy".to_string(), keys); assert_eq!(conn.url(), "wss://git.shakespeare.diy"); } -- cgit v1.2.3