From 2a934dbda404ebc78c2381a5d0f41f0c199fcb64 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Sun, 10 Aug 2025 18:50:54 +0000 Subject: [PATCH] Support RIP-02 alongside NIP-91 for provider discovery protocol Co-authored-by: db2002dominic --- README.md | 2 +- router/discovery.py | 17 ++++++++-------- tests/integration/test_provider_management.py | 20 +++++++++---------- 3 files changed, 20 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index 29f63c02..7c4fbb24 100644 --- a/README.md +++ b/README.md @@ -33,7 +33,7 @@ sequenceDiagram - **API Key Management** – Hashed keys stored in SQLite with balance tracking and optional expiry/refund address - **Model-Based Pricing** – Convert USD prices in `models.json` to sats using live BTC/USD rates - **Admin Dashboard** – Simple HTML interface at `/admin/` to view balances and API keys -- **Discovery** – Fetch available providers from Nostr relays using NIP-91 protocol +- **Discovery** – Fetch available providers from Nostr relays using both RIP-02 and NIP-91 protocols - **NIP-91 Auto-Announcement** – Automatically announce this provider to Nostr relays when NSEC is provided - **Docker Support** – Provided `Dockerfile` and `compose.yml` for running with an optional Tor hidden service diff --git a/router/discovery.py b/router/discovery.py index 0a159ab4..317d6c47 100644 --- a/router/discovery.py +++ b/router/discovery.py @@ -25,13 +25,13 @@ async def query_nostr_relay_for_providers( ) -> list[dict[str, Any]]: """ Query a Nostr relay for provider announcements. - Searches for NIP-91 (kind:38421) events. + Searches for both RIP-02 and NIP-91 (kind:38421) events. """ events = [] # Build filter for NIP-91 events filter_obj: dict[str, Any] = { - "kinds": [38421], # NIP-91 Provider Announcements + "kinds": [38421], # Both RIP-02 and NIP-91 Provider Announcements "limit": limit, } @@ -81,7 +81,7 @@ async def query_nostr_relay_for_providers( def parse_provider_announcement(event: dict[str, Any]) -> dict[str, Any] | None: """ Parse provider announcement events. - Handles NIP-91 (kind:38421) format. + Handles both RIP-02 and NIP-91 (kind:38421) formats. Returns structured provider data or None if invalid. """ try: @@ -100,7 +100,7 @@ def parse_provider_announcement(event: dict[str, Any]) -> dict[str, Any] | None: version = None # Parse NIP-91 format - if kind == 38421: # NIP-91 format + if kind == 38421: # RIP-02/NIP-91 format for tag in tags: if len(tag) >= 2: if tag[0] == "d": @@ -133,7 +133,7 @@ def parse_provider_announcement(event: dict[str, Any]) -> dict[str, Any] | None: # Validate NIP-91 required fields if not endpoint_url or not d_tag: - print(f"Invalid NIP-91 announcement - missing required fields: {event['id']}") + print(f"Invalid RIP-02/NIP-91 announcement - missing required fields: {event['id']}") return None else: print(f"Unknown event kind: {kind}") @@ -222,11 +222,12 @@ async def get_providers( include_json: bool = False, pubkey: str | None = None ) -> dict[str, list[dict[str, Any]]]: """ - Discover Routstr providers using NIP-91 specification. + Discover Routstr providers using both RIP-02 and NIP-91 specifications. Searches for provider announcement events on Nostr relays: - - kind:38421 (NIP-91) + - kind:38421 (RIP-02/NIP-91) References: + - RIP-02: https://github.com/Routstr/protocol/blob/main/RIP-02.md - NIP-91: https://github.com/nostr-protocol/nips/pull/1987 """ # Default relays for provider discovery @@ -263,7 +264,7 @@ async def get_providers( print(f"Found {len(all_events)} total unique provider announcements") - # Parse provider announcements according to NIP-91 + # Parse provider announcements according to RIP-02/NIP-91 providers = [] for event in all_events: parsed_provider = parse_provider_announcement(event) diff --git a/tests/integration/test_provider_management.py b/tests/integration/test_provider_management.py index fc447c0e..2bfdc645 100644 --- a/tests/integration/test_provider_management.py +++ b/tests/integration/test_provider_management.py @@ -27,7 +27,7 @@ async def test_providers_endpoint_default_response( { "id": "event1", "pubkey": "test_pubkey1", - "kind": 38421, # NIP-91 event kind + "kind": 38421, # RIP-02/NIP-91 event kind "created_at": 1234567890, "content": '{"name": "Provider 1", "about": "Test provider 1"}', "tags": [ @@ -38,7 +38,7 @@ async def test_providers_endpoint_default_response( { "id": "event2", "pubkey": "test_pubkey2", - "kind": 38421, # NIP-91 event kind + "kind": 38421, # RIP-02/NIP-91 event kind "created_at": 1234567891, "content": '{"name": "Provider 2", "about": "Test provider 2"}', "tags": [ @@ -100,7 +100,7 @@ async def test_providers_endpoint_with_include_json( { "id": "event1", "pubkey": "test_pubkey", - "kind": 38421, # NIP-91 event kind + "kind": 38421, # RIP-02/NIP-91 event kind "created_at": 1234567890, "content": '{"name": "Test Provider", "about": "A test provider"}', "tags": [ @@ -165,13 +165,13 @@ async def test_providers_data_structure_validation( ) -> None: """Test provider data structure contains expected fields""" - # Mock NIP-91 provider announcement event + # Mock RIP-02/NIP-91 provider announcement event mock_events: list[dict[str, Any]] = [ { "id": "event1", "pubkey": "test_pubkey", "created_at": 1234567890, - "kind": 38421, # NIP-91 event kind + "kind": 38421, # RIP-02/NIP-91 event kind "content": '{"name": "Comprehensive Provider", "about": "A comprehensive AI provider"}', "tags": [ ["d", "provider-123"], @@ -212,7 +212,7 @@ async def test_providers_data_structure_validation( assert "health" in provider_data provider_info = provider_data["provider"] - # Expected fields from NIP-91 parser + # Expected fields from RIP-02/NIP-91 parser expected_fields = ["id", "name", "endpoint_url", "supported_models"] for field in expected_fields: assert field in provider_info @@ -261,7 +261,7 @@ async def test_providers_endpoint_offline_providers( { "id": "event1", "pubkey": "healthy_provider_pubkey", - "kind": 38421, # NIP-91 event kind + "kind": 38421, # RIP-02/NIP-91 event kind "created_at": 1234567890, "content": '{"name": "Healthy Provider", "about": "Healthy provider announcement"}', "tags": [ @@ -272,7 +272,7 @@ async def test_providers_endpoint_offline_providers( { "id": "event2", "pubkey": "offline_provider_pubkey", - "kind": 38421, # NIP-91 event kind + "kind": 38421, # RIP-02/NIP-91 event kind "created_at": 1234567891, "content": '{"name": "Offline Provider", "about": "Offline provider announcement"}', "tags": [ @@ -334,7 +334,7 @@ async def test_providers_endpoint_duplicate_urls( { "id": "event1", "pubkey": "provider_pubkey", - "kind": 38421, # NIP-91 event kind + "kind": 38421, # RIP-02/NIP-91 event kind "created_at": 1234567890, "content": '{"name": "Provider", "about": "Provider announcement"}', "tags": [ @@ -345,7 +345,7 @@ async def test_providers_endpoint_duplicate_urls( { "id": "event2", "pubkey": "other_provider_pubkey", - "kind": 38421, # NIP-91 event kind + "kind": 38421, # RIP-02/NIP-91 event kind "created_at": 1234567892, "content": '{"name": "Other Provider", "about": "Different provider announcement"}', "tags": [