nostr-transport: lnurlw_create_link returns lnurl=null; consumers forced to compose the bech32 themselves #1
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Context
When consumers call
lnurlw_create_linkvia the nostr-transport kind-21000 RPC, the returnedWithdrawLinkhaslnurlandlnurl_urlset toNone. The HTTP path populates those fields; the nostr path does not.Concrete comparison:
views_api.py:41-48(HTTP — populateslnurl):transport_rpcs.py:40-46(nostr — leaveslnurl=None):helpers.create_lnurl(link, req)requires a FastAPIRequest(forreq.url_for(...)) and so can't be invoked from the nostr RPC handler. The result is thatlink.lnurlandlink.lnurl_urlcome back as their defaultNonevalues.Impact on downstream consumers (bitspire /
aiolabs/lamassu-next)Surfaced 2026-06-01 during a joint smoke for the operator-fee-config flow (lamassu-next#57). The ATM does:
(
apps/machine/src/services/lightning.ts:692-711)Every ATM is therefore provisioned with a separate
VITE_LNBITS_HTTP_URLenv var (also surfaces aslnbitsHttpUrlindeploy/nixos/bitspire-atm.nix). Three downsides:request.url_root; over nostr, every consumer has to be told.It also confused a sibling session today during smoke-test triage, since the bitspire boot script echoes
LNbits HTTP: ${lnbitsHttpUrl}— reads like ATM-→-LNbits connectivity (which is 100% nostr post-path-B), when in fact the value is only used to embed a customer-wallet callback URL into LNURL QRs. The variable is load-bearing for cash-in despite all RPC traffic being over nostr.Proposed fix
In
transport_rpcs.handle_lnurlw_create_link(and any sibling RPC that today wraps an HTTP-views-populated URL field), compose the URL fromsettings.lnbits_baseurlinstead ofreq.url_for(...), then bech32-encode it the same wayhelpers.create_lnurldoes.Sketch:
When
lnbits_baseurlis unset, retain the current behavior (returnNone) so consumers can still fall back to their own composition — but when it IS set (i.e., on every real deployment), the RPC populates the field.The same shape applies to:
handle_lnurlw_get_linkhandle_lnurlw_update_linkWithdrawLinkshapeSister extensions to audit
Any nostr-transport RPC wrapping an LNbits extension that today populates a URL via the HTTP-views layer is likely affected the same way. At minimum:
aiolabs/lnurlp—lnurlp_create_linklikely returns a null lnurl on the nostr pathWorth a quick grep across the fork set for
req.url_for/request.url_rootcalls insidehelpers.pyfiles alongsidetransport_rpcs.pyfiles that don't passrequestthrough.Consumer-side benefit
For bitspire specifically (lamassu-next#57 follow-up): drops
VITE_LNBITS_HTTP_URLfrom/var/lib/bitspire/.env, removes thelnbitsHttpUrloption fromdeploy/nixos/bitspire-atm.nix, removes the misleading boot echo, and letsapps/machine/src/services/lightning.ts:710collapse toconst lnurl = link.lnurl; if (!lnurl) throw new Error(...).Other downstream consumers of the nostr-transport API get the same simplification.
Cross-refs
17:46Zand the correction §18:00Z.aiolabs/lamassu-next:apps/machine/src/services/lightning.ts:692-711andaiolabs/lamassu-next:deploy/nixos/bitspire-atm.nixoptionlnbitsHttpUrl.aiolabs/lamassu-next#57(operator-fees-over-nostr; the smoke that surfaced this).