Repository navigation
Releases: ArkLabsHQ/lnurl-server
Releases · ArkLabsHQ/lnurl-server
Release list
v0.5.0
Added
@arkade-os/lnurl-client0.8.0: the glue a wallet otherwise writes itself —createLocalStorageLnurlWatermarkStore(key?)from/browseris the cursor backendcreateLnurlPaymentRepositoryneeds beside the IndexedDB record store: one JSON map of${baseUrl}|${lightningAddress}cursors underkey, where a map that cannot be read counts as empty so the next sync replays rather than skips.lnurlActivityResolverandsentActivityResolverawait their getter on everyprepare, so() => repository.all()passes straight in.arkadeLnurl().sessionLnurl()returns the LNURL a nameless claim will serve, before the claim lands.chainName(chainId)names a CAIP-2 chain as the BTCPay LNURLVerify plugin does. It also adds integration helpers:createLocalStorageLnurlSentPaymentStore(key?)in/browserbackscreateLnurlSendTracker, andtracker.subscribe(listener)fires after each write that landed.syncPaymentsandreceiver.sync()take asignalthat stops fetching and writing.lnurlActivity(activity)reads a tagged activity row back typed, withLNURL_ACTIVITY_KIND/SENT_ACTIVITY_KINDexported.LnurlAddressErrorCodetypesLnurlError.codefor address endpoints, held to the server's codes by a type-level test. For payers,lnurlPayableRanges(payRequest)gives the sendable ranges and whether an amount fits, andfirstQuote(options)withisLnurlRailfalls back from a rail that cannot quote. For token receive,tokenOptionAcceptsapplies an option's bounds andrequestTokenQuoterequests, checks and normalizes a quote with a minimum lifetime. Receiver discovery moves into the facade:owned({ refresh: true })re-binds the current destinations only on active aliases the server lists as bound elsewhere, or on every alias where the server does not say;claim,ownedandupgraderun one at a time per identity and server across facades; asignalpassed toownedorclaimrejects with its reason and stops further writes;addresses()anddomainread without side effects;bootstrapReceiverpassesrefreshandsignalthrough.- The owner's address list reports where each address pays —
GET /lnurl/addressaddsarkadeAddress,claimPublicKeyandboardingAddress(null until bound), so a client re-binds only an alias whose destination moved.
Changed
arkadeLnurl().owned()returns only an active address; it no longer falls back to a revoked one when nothing else is owned. It also waits for aclaim,ownedorupgradestill running for the same identity and server, and an object typed asArkadeLnurlnow needsdomainandaddresses().
Removed
- HD Lightning-address receiving — the server learnt each receiver's branch xpub and allocated its indexes itself, so it is gone rather than deprecated: the
hdbody ofPOST /lnurl/address/:handle/arkade(a body carrying onlyhdgets the same 400 as one missingarkadeAddress),GET /lnurl/address/:handle/arkade/allocations, and in@arkade-os/lnurl-clientthe/hdentry,listHdAllocations,Receiver.allocations(), thehdoption ofregisterArkadeIdentityandarkadeLnurl, theHdReceiveConfig,HdReceiveAllocationandHdReceiveAllocationPagetypes and the@scure/bip32dependency. Migration 27 drops the HD tables and theaddresses.hd_receivecolumn, allocation history included. An address registered in HD mode has no Arkade destination until its owner binds a static one again witharkadeAddressandclaimPublicKey; rotating means re-binding, whichowned({ refresh: true })already does.
Fixed
- API docs (public and admin) load again: Redoc is pinned to v2.5.4, since
latestnow serves the 3.x ES-module bundle, which a classic script tag cannot run.
v0.4.0
Added
- Share token quote URI/memo validation and normalization, receive-rail limits and destination checks, receiver bootstrap orchestration, and an optional IndexedDB backend through the LNURL client. The demo uses the shared startup, bounds and token payment helpers.
- Add reusable LNURL client payment repositories with injected record/cursor backends and a reset write gate, pending receiver confirmations, and Arkade send tracking with resume and reset cancellation. The demo wallet shares the package confirmation loop. Batched verification now honors the client's injected fetch transport.
- HD Lightning-address receiving — register a dedicated public branch instead of a fixed Arkade identity. Every callback reserves a fresh payout address and claim key, including offline invoices, with durable counters and owner-only allocation history for recovery. HD receivers refuse static registration and fallback; existing static receivers keep their behavior. Migration 26 retains allocation snapshots across configuration changes. The client adds
/hdbranch-export and contract-recovery helpers; applications must watch and shield the resulting payouts. @arkade-os/lnurl-client0.7.0: pay in a unit —requestInvoice(payRequest, { unit: "USD", amount: 500 })pays $5.00 to a receiver that advertisesunits:amountis in the unit's smallest denomination, the answer'spaymentQuotemust be for exactly that amount and unit, and the invoice must be for the quoted millisats. The payment router takes the same asunitAmountbesideraw, andarkadeLnurl().options()accepts aUnitAmountin place of sats; the lnurl rails price it on arkade or lightning and record it aslnurlQuoteMeta(quote).quote.SentPayment, synced received payments and both activity resolvers carryquote, shown asrequested("5.00 USD").parseUnitAmount/formatUnitAmountconvert typed decimals. Breaking:RequestInvoiceOptionsis now sats or unit, so{ amountSat, unit }no longer compiles; it never worked, since the amount went out in millisats labelled as the unit and the quoted invoice was then refused.- Quoted payments keep their unit amount — a callback priced in a LUD-XX unit records
{ amount, unit, decimals }on its settlement (migration 25), returned asquoteby the owner's payments sync and shown in the admin Settlements view. - Unit quotes on the arkade and onchain destination rails —
unitwas refused there; only token-deposit rails, which quote their own payment, still refuse it. The demo wallet's send screen offers a target's units beside sats. - Solvers listed by pointer, with their markets read over Nostr — a registry index's
solvers, and any pasted or file card carrying adiscovery_pubkeyand relays, are resolved on every refresh from the signed card each solver publishes over Nostr (kind 38859,d= the network); a card that passes every check replaces that solver's listed markets. To allow a solver the registry does not list, paste its signed pointer — a card with no markets, fromcli card --pointeron intent-solver — in the Solvers tab. Unreadable relays leave discovery as it was, with a warning. - Price feeds for
unitsquoting (QUOTE_UNITS) — the LUD-XX paymentQuote seam finally has a provider: each listed unit is priced from its own JSON feed (QUOTE_<CODE>_FEED_URL,QUOTE_<CODE>_DECIMALS, andQUOTE_<CODE>_PRICE_PATHunless the URL is Binance or CoinGecko), the feed contract intent-solver's asset markets use, with exact decimal math and the msat amount rounded up. Feeds refresh everyQUOTE_REFRESH_MS(60 s) in the background; a unit more than three intervals stale is neither advertised nor quoted, and/healthreports it as the optionalpriceFeedscheck. Session LNURLs now advertise and quoteunitstoo, andpriceFeedQuotesis exported forcreateServerembedders. Unset, nothing changes. WithDB_PATH, units are also editable at runtime from the admin UI's Quotes tab and/admin/api/quote-units(migration 24 addsquote_units): an admin unit replaces the env unit of the same code, a feed must answer before it is saved, and admin-entered feed URLs are fetched through an SSRF guard that refuses private, loopback and link-local hosts. @arkade-os/lnurl-client0.6.0 — small receive-wallet and payment-router interfaces replace concrete SDK classes, with optional router injection and isolated SDK RC compatibility checks. The optional SDK peer now requires>=0.5.0-rc.0; callers explicitly typing returned routers as the SDK'sPaymentRoutershould useLnurlPaymentRouterinstead. The main entry point remains SDK-free.- Optional CAIP-19 identifiers for BTC payment options and network-checked callback aliases, preserving bare rail selectors and token deposit options.
- Compiler-generated v3 covenant destinations with permissionless batch renewal, separate fee funding, and durable successor tracking. Existing v1/v2 funded addresses retain their original construction.
- Token deposits through FixedFloat (mainnet only) — with
FIXEDFLOAT_API_KEYandFIXEDFLOAT_API_SECRETset, an address with an Arkade identity advertises onepaymentOptionsentry per stablecoin network (ff-usdtarbitrum,ff-usdcsol,ff-usdttrc, … — USDT and USDC on Ethereum, Arbitrum, Base, Optimism, Polygon, Avalanche, Solana and Tron), each withtypeset to its CAIP-2 namespace, a CAIP-19asset, aunitnaming aunits[]entry, andprovider: "FixedFloat". The callback creates the corridor swap's hold invoice and a fixed-rate FixedFloat order paying it, and answers the deposit address, an EIP-681 or Solana PaypaymentURI(none on Tron), and apaymentQuotein the token's base units whoseexpiresAtis the payer's deadline: the earlier of FixedFloat's deadline and the corridor invoice's lessFIXEDFLOAT_SETTLE_MARGIN_SECONDS(600 s), refused when that leaves underFIXEDFLOAT_MIN_PAY_WINDOW_SECONDS(300 s). There is no top-levelexpiresAton these answers. Settlement is the corridor's alone;verifythen reports the payer's deposit txid aspaymentReference. The server never holds or refunds the payer's tokens: a late or wrong deposit is resolved with FixedFloat. Known risk: FixedFloat may keep an order open past our deadline, and a payer who deposits in that gap may find the invoice dead and is not paid out; FixedFloat then holds the tokens in its emergency/refund flow. Expired orders stay watched until the TTL, so such a deposit is logged and shown in the admin view. It refuses to start with FixedFloat keys on any network butbitcoin, without a file-backedDB_PATH, or without offline receive. Every FixedFloat call shares one budget kept 25 units under FixedFloat's 250 a minute. The callback answers 429 past it, pastFIXEDFLOAT_ORDERS_PER_IPtoken callbacks from one IP (10 per 10 minutes), pastFIXEDFLOAT_MAX_OPEN_ORDERS_PER_ADDRESSunfunded orders for one receiver (5), or pastFIXEDFLOAT_MAX_OPEN_ORDERSin all (20). Each token callback also spends one of the 30 callbacks a minute per IP that the offline Lightning and Arkade destination callbacks share, a limit with no setting, so a burst of them can refuse the same IP's other callbacks for the rest of that minute. After an HTTP 429 from FixedFloat, or five errors in a row, all FixedFloat calls pause for two minutes and the options readavailable: false. Two unroutable-invoice answers (304) in a row close one receiver's token callbacks for ten minutes. Migration 18 adds theff_orderstable; the admin rails and settlements views show the rail and each order (never its token). FIXEDFLOAT_SIMULATE=true— an in-process stand-in for test networks (refused onbitcoinand with real keys) that answers the same shapes underffsim-ids,provider: "Simulated", on testnet chains, so wallet and checkout UIs can be exercised without real tokens. It takes no deposit.@arkade-os/lnurl-client0.5.0: token options —PaymentOptiongainsasset,unitandprovider; a destination result now carriespaymentURI(always sent, previously dropped),paymentQuote,paymentDestinationTag,expiresAtandprovider.tokenOptions(payRequest)parses token options exactly as the BTCPay LNURLVerify plugin does, andrequestInvoiceon one checks the answer as the plugin does: the option asked for, an address on the token's chain, and a whole amount of the token.- The admin Settlements view links each destination to its explorer and copies it whole on click — an Arkade destination opens in the Arkade explorer and an on-chain one in mempool, for the network solver discovery reports (no link while discovery is off). Clicking the shortened value copies the full destination, or a Lightning row's payment hash.
verifiableon every advertised payment option — whether that option's callback answers carry a LUD-21verifyURL, proposed for LUD-XX (lnurl/luds#303). Until now a payer that must detect settlement, such as a merchant backend, learnt that only by requesting a destination, and lnurl-server's onchain option never has one: every checkout cost a refused request, a settlement row and a slot of the per-IP destination budget. Lightning istrue; arkade istrueonly while per-payment covenant destinations serve it, since the static-address fallback identifies no payment; onchain isfalse. An address served without server caps states nothing, because absent means unknown.@arkade-os/lnurl-client0.4.5 types it asPaymentOption.verifiable.lnurlQuoteMeta(quote)and theLnurlQuoteMetatype (@arkade-os/lnurl-client/arkade) — what an lnurl rail records on its quote (target,via,verify,verifyBatch), readable without castingquote.meta.lnurl. Undefined for a quote no lnurl rail made.SentPayment.verify/verifyBatch— the receiver-confirmation URLs a send's quote carried, so an app can resume a confirmation that ...
v0.3.4
Fixed
- The served spec omitted the health endpoints —
GET /livezandGET /readyz(with its{ status, components, reason? }body and the 503 it answers while unready) were registered bysrc/server.tsbut absent fromsrc/openapi.ts, so neither the Redoc page nor/openapi.jsonmentioned the probes an orchestrator uses. Both are now documented. - The public spec under-described the SSE session contract —
POST /lnurl/sessionnow documents the optional{ token }body that makes a session reconnectable, the 400 / 409 / 429 rejections returned before the stream opens, and theerrorstream event. The LUD-16 callback's bolt11 branch now includes thepaymentOption: "lightning"echo, and the Arkade-identity route's 400 describes both of its invalid-input cases. - The admin spec was thinner than the code on solver cards and API keys —
PUT /solver-cards/{id}had no request body at all, and the PUT/PATCH/DELETE responses omitted the 400/404 failures the routes return;GET /solver-cardsand the mutation responses now carry the persisted card shape. API-key responses include thecreatedAt/lastUsedAtfields the repo returns. (Path and method coverage itself was audited against the router and already complete — the docs/spec meta endpoints stay undocumented by design.)
Added
- A route/spec drift guard (
test/openapi-routes.test.ts) — builds the real apps withcreateServerandcreateAdminServer, walks the Express 5 router stacks, and asserts both that every registered method+path pair is documented in the matching spec and that every documented operation is registered, with an explicit allowlist for the docs/spec meta endpoints (GET /,GET /openapi.json, adminGET /docs). It also fetches both specs through the real HTTP apps, so a spec that no longer serialises fails the suite too.
v0.3.3
Added
- Request tracing (
TRACE_REQUESTS=1, default off) — one structured completion line per request through the existing logger, on both the public and admin servers, including unmatched routes Express answers with a 404, so an operator behind a reverse proxy can tell "no request arrived" from "a request arrived and matched nothing". The line carries method, path,Host, response status, duration in ms, theX-Request-Idthe app attaches, and theX-Forwarded-For/X-Forwarded-Proto/X-Real-Ipheaders. With the flag on, startup also logs the resolved public config (base URL, ports, admin bind,trustProxy, Arkade network and indexer URL, discovery sources/candidates and readiness). Off, no lines are added.
Fixed
- The OpenAPI spec claimed
0.2.6on every release since 0.3.0 —src/openapi.tshardcodedinfo.version, so/openapi.jsonon the 0.3.2 image still reported0.2.6, and the admin spec, which sources its version from the public spec, inherited it. The version now lives in one literal (src/version.ts) used by both specs and the startup log, and a test pins it topackage.jsonso a release that updates only one of the two fails CI. Thearkade-lnurl listening on …line now carries the version, sodocker logstells an operator which build is running without probing a route.
v0.3.2
Fixed
- Offline receive matched none of the registry's corridor cards — solver selection queried the market set by exact leg id (
arkade:<network>/slip44:<slip>/bolt11:<network>/slip44:<slip>), but the published registry indexes its corridor cards in the legacy v0 shape (base_asset.id: "btc"+quote_corridor: "lightning"), whose leg keys render asarkade:btc/lightning:btc.selectMarketscompares leg keys as strings, so the two never met and every offline receive answeredno solver card supports a N sat lightning receive. Selection now delegates tosolverLightningRendezvous, which accepts both id forms and also rejects a market whose base leg is not BTC — a check the corridor test alone cannot make. The runner-up candidates are still returned in ranked order, so per-quote solver failover is unchanged; the emulator x-only key is now a required argument, since the covenant commits to it. A regression test pins the registry's live short-id shape.
v0.3.1
Fixed
- Covclaimd-optional offline receive —
OFFLINE_SELF_CLAIM=truewithOFFLINE_EMULATOR_URLno longer requiresCOVCLAIMD_URL. The RFQ omits the optionalclaim_packetand the solver funds anyway, waiting for this server's own covenant claim;OFFLINE_COVENANT_DESTINATIONSlikewise needs onlyARK_SERVER_URL+OFFLINE_EMULATOR_URL.
v0.3.0
Changed
- Latest SDKs —
@arkade-os/sdk0.4.68 → 0.4.72 (@arkade-os/solver-discoverystays at 0.2.4, already latest). The offline-receive corridor client moves off the vendoredsrc/vendor/arkade-swap/copy onto the published@arkade-os/swap@0.0.18, which now ships thelightning:BTC -> arkade:BTCreceive corridors. Adapts to the SDK's watch-only contract events (isContractVtxoEventgate insrc/covenant-watcher.ts).
Added
- Production runtime and card-only solver discovery — uses
@arkade-os/solver-discoveryacross multiple registries, cached indexes, startup cards, and cards pasted into the admin UI. DirectSOLVER_URL/ pubkey / relay configuration is removed. Accepted offline swaps atomically persist solver and VHTLC recovery state for restart-safe polling/self-claim. Adds liveness/readiness endpoints, resource caps, redacted structured logs, graceful shutdown, a non-root image, reference Compose deployment, and an operations runbook. - Self-claim (
OFFLINE_SELF_CLAIM, default off) — the server can push the solver-funded lockup'snonInteractiveClaimleaf itself, while the covenant and emulator still constrain payment to the user's registered address. Claims skip underfunded lockups, are safe to retry, and never block status polling. Versioned VHTLC reconstruction data is now persisted atomically with accepted swaps, so self-claim resumes after restart. - Per-payment Arkade destinations (
OFFLINE_COVENANT_DESTINATIONS, default off) — thearkaderail handed every payer the same registered address, so concurrent payments were indistinguishable on the wire and the watcher correlated them by amount and arrival window; two payments of the same amount could not be told apart at all, and a greedy first-fit could let an older, smaller record consume a larger one's payment. With the flag set each callback derives its own address (src/covenant-destination.ts) and the script becomes the identifier: a VTXO there belongs to exactly one record, sosrc/covenant-watcher.tssettles it on the contract event instead of guessing (the arrival window, the 15s skew and the oldest-first assignment all drop away; the amount check stays, since an under-payment must not flipsettled).src/arkade-watcher.tsskips these records and keeps correlating the static-address rail only, where amount and arrival time remain all there is. The address is three Taproot leaves —condition(H(P))+ operator + a cosigner key committing toenforcePayTo(static address); user + operator; and user alone after a CSV (OFFLINE_COVENANT_RECOVERY_DELAY_SECONDS, default 86528 — BIP68 counts seconds in 512s units, so 24h exactly is not expressible). Only the first carriesH(P), so a fresh preimage moves the address while the covenant bytes stay fixed, and the preimage is not a secret: the covenant means anyone holding it can only pay the user, which is why the two recovery leaves are keyed to the user and need neitherPnor this server. A sweeper (src/covenant-sweeper.ts) moves each funded destination on to the static address, so the receiving wallet sees an ordinary arrival and needs to know nothing about the covenant. Costs one extra Arkade transaction per payment and a dependency on the emulator co-signing the sweep; requiresOFFLINE_EMULATOR_URL,COVCLAIMD_URLandARK_SERVER_URL, all checked at startup rather than when a payer is already holding an address. Derivation failure at request time falls back to the static address — ambiguous again for that payment, which beats refusing to be paid. Tracking, watching and spending are the SDK's: the covenant is registered as a contract type (src/covenant-contract.ts) soContractManagerowns its lifecycle — a subscription instead of a poll,awaiting-fundsdemoting itself once a payment lands, andgetSpendablePathschoosing the leaf. Adds migration 007 (covenant_scriptwith a unique index) as the join key; everything else about a covenant lives in the SDK's ownark_contracts. - ClaimPacket TLV codec (
src/claim-packet.ts) — encode/decode for the Arkade extension packet body covclaimd matches on (type0x04; TLVs0x01ciphertext,0x02arkade_script,0x03covclaimd_pub_key), transcribed from covclaimd'spkg/preimage/packet.go. Groundwork for the non-interactive claim path in which the funder stamps the packet into the lockup's funding transaction instead of revealing it to covclaimd over HTTP (arkade-os/intent-solver#46); Wired behindOFFLINE_STAMP_CLAIM_PACKET(default off): with it set, the callback sends0x01ciphertext +0x03covclaimd_pub_key instead of the bare ciphertext, so the solver stamps the packet on chain and the covclaimd is ours to choose rather than something both sides must have been configured with. Off by default because a solver predating that PR forwards the packet as a ciphertext, cannot decrypt it, and the swap funds and refunds. - LUD-21 (
verify) — LNURL-pay callback responses now carry averifyURL, and a newGET /lnurl/verify/:paymentHashlets payers poll settlement status ({ status, settled, preimage, pr }). Because this server is a relay with no Lightning node, wallets report settlement via the new authenticatedPOST /lnurl/session/:id/settledwith{ preimage }— the server checkssha256(preimage)against a payment hash the session issued (decoded locally from the bolt11, no new dependency) before flipping the record to settled. Settlement records live in a newsettlementstable (migration 003) whenDB_PATHis set, or in an in-memory store with aVERIFY_TTL_MS(default 24h) lifetime otherwise, so a payer can still pollverifyafter the wallet disconnects. Available on both the ephemeral LNURL and the LN-address (LUD-16) flows. - Offline receive (opt-in) — with solver cards plus
COVCLAIMD_URLandARK_SERVER_URL, an LN address can register an Arkade receive identity and receive while its wallet is offline. The server obtains a Nostr RFQ through the ranked card candidates, returns the verified hold invoice, and tracks the pinned solver through LUD-21 settlement without holding user keys or funds. - Payment options (LUD-XX,
paymentOptions) — an LN address with a registered Arkade identity now advertisespaymentOptions: [lightning, arkade]in its LUD-06payRequest. A payer selects a rail with?paymentOption=<id>:lightning(or absent) keeps the existing BOLT11 / offline-swap flow;arkadereturns the user's Arkade address as apaymentDestination(direct receive, no swap) plus averifyURL that reports the non-prLUD-21 shape ({ settled, paymentOption, paymentDestination, paymentReference }). Unknown/unavailable options return{ status: "ERROR", reason: "Unsupported paymentOption" }. Settlement records carry the option + destination + agreed amount (migration 006, incl.amount_msatso the future watcher can correlate observed payments). Because the payer pays the Arkade address directly, the server isn't in the payment path, so arkadesettledflips only when the server-side Arkade watcher observes the payment — which ships in this same release, below. The resolver is a small registry (src/payment-options.ts) so future rails/assets slot in. - Payment quote (LUD-XX,
paymentQuote) — when aQuoteProvideris injected, the LN-addresspayRequestadvertisesunits(e.g. USD), and a callback with?unit=<code>(optionally&receiveUnit=) is quoted: the provider converts the unit amount to the effective msat amount used for the bolt11 / offline swap, and the response echoespaymentQuote({ requested, payment, receive?, fees? }). This server has no rate oracle, so without a providerunitsis omitted and anyunit=request returnsUnsupported unit. Framework only — real rate sourcing, assets, and swap-backed quotes plug in behindQuoteProvider(src/quote-provider.ts). Applies to the amount-denominated Lightning path (relay + offline swap); quoting thearkadedestination rail is a follow-up. - Arkade settlement watcher — when
ARK_SERVER_URLis set, a background poller watches the Arkade indexer for payments to destination-rail records (paymentOptions: arkade) and flipsverifytosettled: truewithpaymentReference= the observed Arkade txid. Matching is by destination script + agreed amount (amount_msat, never under-paid) + arrival window; each observed VTXO settles at most one record, oldest first. Correlation fuzziness (an unrelated same-amount payment in the same window) is inherent to reference-less address payments and is documented in the README. - Solver discovery —
SOLVER_REGISTRY_URLS, a startup card file, and admin-pasted cards merge into one atomically refreshed, fee-ranked discovery snapshot with bounded registry caching. - Admin settlements view —
GET /admin/api/settlements(filter:settled,option,limit) lists LUD-21 settlement records (relay invoices, offline corridor swaps, destination-rail payments) newest-first, never exposing preimages or invoices; plus a Settlements tab in the admin SPA with a 5s live poll.
Security
- Patched express's transitive dependencies —
path-to-regexp8.3.0 → 8.4.2 (high severity),qs6.15.0 → 6.16.0, andbody-parser2.2.2 → 2.3.0, which between them clear every advisory open against code this server actually ships. Lockfile only: express 5.2.1 is already the newest release, and the ranges it androuterdeclare already allowed the patched versions, so nothing inpackage.jsonchanged. The advisories left open after this are all dev-only (vite,postcss,nanoid,esbuild, and theform-dataundersupertest) and none reach the published image, whose final stage installs--prodand copies onlydist.
v0.2.6
Added
- Admin API documentation — a dedicated OpenAPI/Swagger spec for the admin API, served on the admin port at
GET /admin/api/docs(Redoc page) andGET /admin/api/openapi.json. Documents every admin endpoint (domains, addresses, API keys, blacklist, sessions, settings) with request/response shapes and error codes. The admin SPA nav gained an "API Docs ↗" link. The spec's version is sourced from the public spec so the two can't drift. (Kept separate from the public spec by design — the admin API runs on the loopback-bound admin port behind your auth proxy.)
v0.2.5
Fixed
- The OpenAPI/Swagger docs (served at
/) only listed the original four relay endpoints — the public endpoints added in 0.2.0 were never documented, so the page looked unchanged across releases despite the version bump. Documented the missing public endpoints:POST /lnurl/address(register/claim),GET /lnurl/address(list own),DELETE /lnurl/address/{username}(revoke own), and the LUD-16GET /.well-known/lnurlp/{username}+/callbackroutes, with request/response shapes and error codes. (The admin API stays out of the public spec by design — it runs on the separate admin port.)
v0.2.4
Added
- Admin Sessions tab: a live view of every connected wallet (the in-memory SSE session map, polled every 5s), joined to the
addressestable so each connection shows the LN address(es) it belongs to. Per session: bound addresses, reusable-vs-ephemeral type, connected-since, client IP (as seen throughtrust proxy), invoices issued (+ last-issued time), and the in-flight payer request (amount + how long it's been waiting). A Disconnect action force-closes a session (POST /admin/api/sessions/:id/disconnect). The session token is never exposed. The Dashboard gains a "Live sessions" count.