diff options
| author | DanConwayDev <DanConwayDev@protonmail.com> | 2026-01-09 07:57:54 +0000 |
|---|---|---|
| committer | DanConwayDev <DanConwayDev@protonmail.com> | 2026-01-09 07:57:54 +0000 |
| commit | 7cc5d37cbf4f02f0bb7eee6342dc1ede5a841a7b (patch) | |
| tree | 62d3dcf291a7ca67d18cf397b448fb98d62553ba /src/sync/relay_connection.rs | |
| parent | 6bc2d70f6dd351521e522cc4d0f1ac188848ad26 (diff) | |
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
Diffstat (limited to 'src/sync/relay_connection.rs')
| -rw-r--r-- | src/sync/relay_connection.rs | 22 |
1 files changed, 14 insertions, 8 deletions
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 { | |||
| 106 | /// | 106 | /// |
| 107 | /// # Arguments | 107 | /// # Arguments |
| 108 | /// * `url` - The relay URL to connect to (with or without scheme, e.g., "relay.example.com" or "wss://relay.example.com") | 108 | /// * `url` - The relay URL to connect to (with or without scheme, e.g., "relay.example.com" or "wss://relay.example.com") |
| 109 | pub fn new(url: String) -> Self { | 109 | /// * `keys` - Cryptographic keys for NIP-42 authentication (typically the relay operator's keys) |
| 110 | pub fn new(url: String, keys: Keys) -> Self { | ||
| 110 | let normalized_url = Self::normalize_url(&url); | 111 | let normalized_url = Self::normalize_url(&url); |
| 111 | let client = Client::default(); | 112 | let client = Client::new(keys); |
| 112 | Self { | 113 | Self { |
| 113 | url: normalized_url, | 114 | url: normalized_url, |
| 114 | client, | 115 | client, |
| @@ -122,9 +123,10 @@ impl RelayConnection { | |||
| 122 | /// # Arguments | 123 | /// # Arguments |
| 123 | /// * `url` - The relay URL to connect to (with or without scheme, e.g., "relay.example.com" or "wss://relay.example.com") | 124 | /// * `url` - The relay URL to connect to (with or without scheme, e.g., "relay.example.com" or "wss://relay.example.com") |
| 124 | /// * `database` - Shared database for local event comparison during negentropy sync | 125 | /// * `database` - Shared database for local event comparison during negentropy sync |
| 125 | pub fn new_with_database(url: String, database: SharedDatabase) -> Self { | 126 | /// * `keys` - Cryptographic keys for NIP-42 authentication (typically the relay operator's keys) |
| 127 | pub fn new_with_database(url: String, database: SharedDatabase, keys: Keys) -> Self { | ||
| 126 | let normalized_url = Self::normalize_url(&url); | 128 | let normalized_url = Self::normalize_url(&url); |
| 127 | let client = Client::default(); | 129 | let client = Client::new(keys); |
| 128 | Self { | 130 | Self { |
| 129 | url: normalized_url, | 131 | url: normalized_url, |
| 130 | client, | 132 | client, |
| @@ -553,19 +555,22 @@ mod tests { | |||
| 553 | 555 | ||
| 554 | #[test] | 556 | #[test] |
| 555 | fn test_new_normalizes_url() { | 557 | fn test_new_normalizes_url() { |
| 556 | let conn = RelayConnection::new("relay.example.com".to_string()); | 558 | let keys = Keys::generate(); |
| 559 | let conn = RelayConnection::new("relay.example.com".to_string(), keys); | ||
| 557 | assert_eq!(conn.url(), "wss://relay.example.com"); | 560 | assert_eq!(conn.url(), "wss://relay.example.com"); |
| 558 | } | 561 | } |
| 559 | 562 | ||
| 560 | #[test] | 563 | #[test] |
| 561 | fn test_new_preserves_wss_scheme() { | 564 | fn test_new_preserves_wss_scheme() { |
| 562 | let conn = RelayConnection::new("wss://relay.example.com".to_string()); | 565 | let keys = Keys::generate(); |
| 566 | let conn = RelayConnection::new("wss://relay.example.com".to_string(), keys); | ||
| 563 | assert_eq!(conn.url(), "wss://relay.example.com"); | 567 | assert_eq!(conn.url(), "wss://relay.example.com"); |
| 564 | } | 568 | } |
| 565 | 569 | ||
| 566 | #[test] | 570 | #[test] |
| 567 | fn test_new_preserves_ws_scheme() { | 571 | fn test_new_preserves_ws_scheme() { |
| 568 | let conn = RelayConnection::new("ws://relay.example.com".to_string()); | 572 | let keys = Keys::generate(); |
| 573 | let conn = RelayConnection::new("ws://relay.example.com".to_string(), keys); | ||
| 569 | assert_eq!(conn.url(), "ws://relay.example.com"); | 574 | assert_eq!(conn.url(), "ws://relay.example.com"); |
| 570 | } | 575 | } |
| 571 | 576 | ||
| @@ -573,7 +578,8 @@ mod tests { | |||
| 573 | fn test_new_with_database_normalizes_url() { | 578 | fn test_new_with_database_normalizes_url() { |
| 574 | // This test just verifies the URL normalization works | 579 | // This test just verifies the URL normalization works |
| 575 | // We can't easily test with_database without a real database | 580 | // We can't easily test with_database without a real database |
| 576 | let conn = RelayConnection::new("git.shakespeare.diy".to_string()); | 581 | let keys = Keys::generate(); |
| 582 | let conn = RelayConnection::new("git.shakespeare.diy".to_string(), keys); | ||
| 577 | assert_eq!(conn.url(), "wss://git.shakespeare.diy"); | 583 | assert_eq!(conn.url(), "wss://git.shakespeare.diy"); |
| 578 | } | 584 | } |
| 579 | 585 | ||