lnurl-server beside your
wallet backend to give your users LNURL-pay and Lightning addresses such as
[email protected]. It can also receive Lightning payments into a user’s
Arkade wallet while the wallet is offline. This page shows how to deploy the
server, connect wallets, add Lightning addresses, and turn on offline receive.
The commands target
lnurl-server v0.4.0.
If you run v0.3, back up its database before you upgrade. On its first
start, v0.4.0 converts the database for SDK 0.5, and v0.3 can’t read it
afterwards.
Prerequisites
- Docker and
openssl. - A domain for Lightning addresses (
yourdomain.combelow) and a hostname for the server (pay.yourdomain.com), both behind a reverse proxy with TLS. - For offline receive: a card for an
intent-solverthat serves Lightning receive, and the URL of the emulator that the solver uses.
Start the Server
The server stores wallet tokens in its database, encrypted withTOKEN_ENCRYPTION_KEY, a 32-byte key. Generate the key once and keep it
with your other secrets: every restart must use the same key.
{"status":"ready",...}.
Route https://pay.yourdomain.com to port 3000 through your reverse proxy.
The proxy must set X-Forwarded-Proto: https: the server builds its callback
URLs from it. Keep the admin port (3001) private: it has no
authentication. The command above does not publish it.
Connect a Wallet
A wallet receives through a session: a server-sent events (SSE) stream that the server uses to ask the wallet for invoices. Open every session with the wallet’s own token: a random hex string that the wallet generates once and keeps. It must have an even number of characters, and at least 32. The server derives the session id from the token. The wallet then gets the same LNURL each time it reconnects, and its Lightning address reaches the session. A Lightning address registered from a session opened without a token reports the wallet as offline.- Open the session with
POST /lnurl/sessionand the JSON body{ "token": "<wallet-token>" }. Keep the SSE response connected. - Read
sessionIdandlnurlfrom the first event,session_created. - Display the LNURL.
- On each
invoice_requestevent, readamountMsat, in millisatoshis (divide by 1000 for sats). Create an invoice for that amount with the swap client, as in LNURL and Lightning Addresses. Post{ "pr": "<bolt11>" }to/lnurl/session/<sessionId>/invoicewithAuthorization: Bearer <wallet-token>, within 30 seconds. To decline, post{ "error": "<reason>" }instead, with a reason of at most 500 characters. - When the invoice settles, post
{ "preimage": "<hex>" }to/lnurl/session/<sessionId>/settledwith the same header. Close the SSE connection when the receive screen closes.
@arkade-os/lnurl-client
makes these calls for you: openSession, then answerInvoice or
rejectInvoice for each request, then reportSettled. It can also derive the
token from the wallet key, so the wallet does not have to store it.
To check the stream, open a session from a terminal:
session_created, with the session’s lnurl.
Add a Lightning Address
BOOTSTRAP_DOMAIN creates the domain on first start. Route
https://yourdomain.com/.well-known/lnurlp/* to the server, and keep the Host
header: the server picks the domain from it. Then register a name with the
wallet’s token:
"lightningAddress": "[email protected]". While
the wallet’s session is connected, each payment to the address sends an
invoice_request to the wallet.
Receive While the Wallet Is Offline
When the wallet’s session is down,lnurl-server quotes the receive route
itself and claims the payment for the wallet.
Keep OFFLINE_SELF_CLAIM on. lnurl-server claims each funded lockup
itself, through the covenant’s claim leaf. The claim needs no key. The Arkade
operator and the emulator sign the leaf, it is gated on the preimage
lnurl-server already holds, and it can pay only the wallet’s address. An
unclaimed lockup goes back to the solver at refund_locktime, and the payer is
refunded. OFFLINE_EMULATOR_URL must be the solver’s emulator, the
EMULATOR_URL the solver runs with, because the covenant commits to its key.
Save the solver’s card in solver-cards.json, as a JSON array. The server also
reads the published solver registry for the operator’s network, and can use any
solver there whose emulator matches OFFLINE_EMULATOR_URL. To use only the
card you saved, add -e SOLVER_REGISTRY_URLS= to the command below.
Recreate the container with the offline settings:
https://emulator.yoursolver.com with the solver’s emulator URL.
/readyz answers ready even when no card can serve Lightning receive. Check
components.solverDiscovery in its response: ok is true when at least one
card can.
Register the Arkade address and the claim public key that the wallet owns, for
each Lightning address:
claimPublicKey is a compressed public key: 02 or 03 followed by 64 hex
characters. For each payment, the service generates the preimage, selects the
solver, returns its hold invoice, and tracks settlement. The claim pays only the
Arkade address committed in the covenant.
A wallet can register a public key branch instead, so that each payment gets
its own Arkade address and claim key. An address in this HD mode never uses the
wallet’s session, cannot go back to a single Arkade address, and leaves the
wallet to restore and watch every payout. See
HD receiving.
lnurl-server operations
Production Compose file, reverse proxy, backups, restore, and upgrades.