fix(pairing): default relay to the transport's nostrrelay, not nostrclient proxy
Some checks failed
ci.yml / fix(pairing): default relay to the transport's nostrrelay, not nostrclient proxy (pull_request) Failing after 0s
Some checks failed
ci.yml / fix(pairing): default relay to the transport's nostrrelay, not nostrclient proxy (pull_request) Failing after 0s
The nostrclient endpoint is a subscription MULTIPLEXER, not a full relay: its
router forwards a client's EVENT upstream but never returns an OK ack (see
nostrclient/router.py). A transport client that awaits OK on publish therefore
times out ("publish timed out"), so kind-21000 RPCs never complete — verified
on the Sintra: connect succeeded but list_wallets hung, and switching to the
nostrrelay endpoint made the whole flow work (wallet, balance, availability).
default_relay_endpoint now derives from settings.nostr_transport_relays — the
relay the transport actually listens on: use it as-is when already
machine-reachable, or re-home its path on lnbits_baseurl when it's a co-located
loopback relay (the bundled nostrrelay). Validation/localhost-reject and the
pair-dialog pre-fill/hint carry over; wording updated to "transport relay".
227 tests pass.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
765b07737b
commit
0bb9939822
6 changed files with 89 additions and 49 deletions
62
pairing.py
62
pairing.py
|
|
@ -199,25 +199,41 @@ def build_seed_url(
|
|||
_WS_SCHEME_RE = re.compile(r"^wss?://", re.IGNORECASE)
|
||||
_LOCAL_HOSTS = {"localhost", "127.0.0.1", "::1", "0.0.0.0"}
|
||||
|
||||
# The nostrclient extension serves a public relay-multiplexer websocket at this
|
||||
# path (see nostrclient/views_api.py `ws_relay`, gated by config.public_ws).
|
||||
NOSTRCLIENT_RELAY_PATH = "/nostrclient/api/v1/relay"
|
||||
|
||||
def _baseurl_ws() -> str | None:
|
||||
"""`settings.lnbits_baseurl` as a ws(s):// scheme+host (no trailing slash)."""
|
||||
base = (settings.lnbits_baseurl or "").strip().rstrip("/")
|
||||
if base.startswith("https://"):
|
||||
return "wss://" + base[len("https://") :]
|
||||
if base.startswith("http://"):
|
||||
return "ws://" + base[len("http://") :]
|
||||
return None
|
||||
|
||||
|
||||
def default_relay_endpoint() -> str | None:
|
||||
"""Derive the machine-facing relay from THIS lnbits' own nostrclient proxy
|
||||
endpoint, so operators configure upstream relays once in the nostrclient
|
||||
extension and every seed points at one stable URL (bitspire#70). Built from
|
||||
`settings.lnbits_baseurl` (http→ws, https→wss). Returns None when the base
|
||||
URL is unset. Requires the nostrclient extension's `public_ws` to be on."""
|
||||
base = (settings.lnbits_baseurl or "").strip().rstrip("/")
|
||||
"""Derive the machine-facing relay from lnbits' nostr-transport config, so a
|
||||
seed points at the relay the transport ACTUALLY listens on (bitspire#70).
|
||||
|
||||
NOTE: this is the nostr *relay* (a full relay that acks EVENTs), NOT the
|
||||
nostrclient proxy — the client multiplexer forwards subscriptions but never
|
||||
sends `OK`, so a transport client that awaits `OK` on publish times out.
|
||||
|
||||
If the transport relay is already machine-reachable, use it as-is; if it's a
|
||||
co-located loopback relay (the bundled nostrrelay at
|
||||
`ws://localhost:5001/nostrrelay/<id>`), re-home its path on lnbits' public
|
||||
base URL so a remote ATM can reach the same relay. Returns None if neither
|
||||
a transport relay nor a base URL is available."""
|
||||
relays = settings.nostr_transport_relays or []
|
||||
if not relays:
|
||||
return None
|
||||
relay = relays[0]
|
||||
host = (urlparse(relay).hostname or "").lower()
|
||||
if host and host not in _LOCAL_HOSTS and not host.endswith(".localhost"):
|
||||
return relay # already remote-reachable
|
||||
base = _baseurl_ws()
|
||||
if not base:
|
||||
return None
|
||||
if base.startswith("https://"):
|
||||
base = "wss://" + base[len("https://") :]
|
||||
elif base.startswith("http://"):
|
||||
base = "ws://" + base[len("http://") :]
|
||||
return base + NOSTRCLIENT_RELAY_PATH
|
||||
return base + urlparse(relay).path
|
||||
|
||||
|
||||
def _validate_relay(url: str, field: str) -> None:
|
||||
|
|
@ -262,11 +278,11 @@ async def pair_spire(
|
|||
with a fake client.
|
||||
|
||||
`relays` are the relays the spire uses for its *own* events
|
||||
(kind-21000/30078). When omitted, they default to this lnbits' nostrclient
|
||||
proxy endpoint (`default_relay_endpoint`, derived from `lnbits_baseurl`) so
|
||||
operators configure upstream relays once in the extension and every seed
|
||||
points at one stable URL. Every relay (and `bunker_relay`) is validated as a
|
||||
remote-reachable `ws(s)://` URL — a loopback host is rejected (bitspire#70).
|
||||
(kind-21000/30078). When omitted, they default to the relay the transport
|
||||
listens on (`default_relay_endpoint`, from `nostr_transport_relays` re-homed
|
||||
on `lnbits_baseurl`) so a seed points at a relay that actually acks EVENTs.
|
||||
Every relay (and `bunker_relay`) is validated as a remote-reachable
|
||||
`ws(s)://` URL — a loopback host is rejected (bitspire#70).
|
||||
`bunker_relay` (the relay baked into `bunker_url`, where the spire reaches
|
||||
the bunker) defaults to `relays[0]`; `keystore_passphrase` defaults to the
|
||||
lnbits bunker setting. All injectable for tests.
|
||||
|
|
@ -284,14 +300,14 @@ async def pair_spire(
|
|||
"LNBITS_NSEC_BUNKER_KEYSTORE_PASSPHRASE is not set — "
|
||||
"cannot mint a spire key"
|
||||
)
|
||||
# Default the spire's event relay(s) to this lnbits' nostrclient proxy
|
||||
# endpoint when the caller gives none.
|
||||
# Default the spire's event relay(s) to the transport's own relay (re-homed
|
||||
# on the base URL) when the caller gives none.
|
||||
if not relays:
|
||||
endpoint = default_relay_endpoint()
|
||||
if not endpoint:
|
||||
raise PairingError(
|
||||
"no relays given and lnbits_baseurl is unset — cannot derive the "
|
||||
"nostrclient relay endpoint for the seed"
|
||||
"no relays given and none could be derived — set "
|
||||
"nostr_transport_relays (and lnbits_baseurl if it's a loopback relay)"
|
||||
)
|
||||
relays = [endpoint]
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue