From 4936eb5084d1c3bf1e90e100670e2600c772592c Mon Sep 17 00:00:00 2001 From: satwise Date: Sun, 3 May 2026 12:08:43 -0400 Subject: [PATCH 1/2] feat(nwcp): add NIP-42 client AUTH support When a NIP-42-enabled relay sends [AUTH, challenge], nwcprovider now responds with a signed kind-22242 event (relay URL + challenge tags). Previously the AUTH message type hit the catch-all raise Exception branch, causing an error log and no response. The relay then closed subscriptions because the challenge was never answered. Fix: add _on_auth_message handler and route msg[0]=AUTH to it in _on_message. Closes: (to be linked after PR filed) Ref: https://github.com/nostr-protocol/nips/blob/master/42.md --- nwcp.py | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/nwcp.py b/nwcp.py index efa5279..1a4d19f 100644 --- a/nwcp.py +++ b/nwcp.py @@ -508,11 +508,38 @@ async def _on_message(self, _, message: str): logger.info("Notice from relay " + self.relay + ": " + str(msg[1])) elif msg[0] == "OK": pass + elif msg[0] == "AUTH": + await self._on_auth_message(msg) else: raise Exception("Unknown message type " + str(msg[0])) except Exception as e: logger.error("Error parsing event: " + str(e)) + async def _on_auth_message(self, msg: list) -> None: + """ + Handle NIP-42 AUTH challenge from the relay (NIP-42). + + When a relay sends ["AUTH", ""], we respond with a signed + kind-22242 event containing the relay URL and challenge string. + See: https://github.com/nostr-protocol/nips/blob/master/42.md + """ + if len(msg) < 2: + logger.warning("Received AUTH message without challenge, ignoring") + return + challenge = msg[1] + auth_event: dict = { + "kind": 22242, + "content": "", + "created_at": int(time.time()), + "tags": [ + ["relay", self.relay], + ["challenge", challenge], + ], + } + self._sign_event(auth_event) + await self._send(["AUTH", auth_event]) + logger.debug("Sent NIP-42 AUTH response for challenge: " + str(challenge)) + async def _connect_to_relay(self): """ Initiate websocket connection to the relay. From 9c4543f1c9f2d86fbd05185be8198144640e3074 Mon Sep 17 00:00:00 2001 From: satwise Date: Tue, 2 Jun 2026 19:58:39 -0400 Subject: [PATCH 2/2] feat(nwcp): log relay OK outcomes for auth diagnostics Implement stakeholder-requested relay auth/publish diagnostics. Changes: - Route incoming OK frames to _on_ok_message instead of ignoring them. - Log rejected OK responses at warning level with relay, event id, and relay reason. - Log accepted OK responses and malformed OK payloads at debug level. Validation summary (no package unit test added, per request): - Ad-hoc runtime validation confirmed AUTH response emits kind 22242 with relay/challenge tags and valid signature. - Log checks confirmed expected rejected/accepted/malformed OK diagnostics are emitted. --- nwcp.py | 35 ++++++++++++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/nwcp.py b/nwcp.py index 1a4d19f..4e0b6aa 100644 --- a/nwcp.py +++ b/nwcp.py @@ -507,7 +507,7 @@ async def _on_message(self, _, message: str): # A message from the relay, mostly useless, but we log it anyway logger.info("Notice from relay " + self.relay + ": " + str(msg[1])) elif msg[0] == "OK": - pass + await self._on_ok_message(msg) elif msg[0] == "AUTH": await self._on_auth_message(msg) else: @@ -515,6 +515,39 @@ async def _on_message(self, _, message: str): except Exception as e: logger.error("Error parsing event: " + str(e)) + async def _on_ok_message(self, msg: list) -> None: + """Handle relay OK messages and surface rejections for diagnostics.""" + event_id = str(msg[1]) if len(msg) > 1 else "unknown" + accepted = msg[2] if len(msg) > 2 else None + relay_msg = str(msg[3]) if len(msg) > 3 else "" + + if accepted is False: + logger.warning( + "Relay " + + self.relay + + " rejected event " + + event_id + + (": " + relay_msg if relay_msg else "") + ) + return + + if accepted is True: + logger.debug( + "Relay " + + self.relay + + " accepted event " + + event_id + + (": " + relay_msg if relay_msg else "") + ) + return + + logger.debug( + "Received malformed OK message from relay " + + self.relay + + ": " + + str(msg) + ) + async def _on_auth_message(self, msg: list) -> None: """ Handle NIP-42 AUTH challenge from the relay (NIP-42).