Skip to content

Repository files navigation

lnurlmint

lnurlmint icon

An LNbits extension that implements lnurlcash (LUD-25) — Lightning bearer assets on top of LUD-03 withdrawRequest and LUD-06 payRequest. A port of the standalone lnurl-mint FastAPI app into the LNbits extension model.

Each LNbits wallet can run its own mint, issuing bearer notes that circulate offline as lnurlw:// withdraw links and can be rotated, split, merged, or melted back to a BOLT-11 payment.

What is a bearer note?

A bearer note is a k1 (a secret preimage) that the mint has credited with value. It is:

  1. Minted by paying a LUD-06 invoice — the payment preimage is the note
  2. Circulated offline as lnurlw://<host>/w?k1=<k1> — no account needed
  3. Redeemed by anyone with a spec-compliant wallet — rotate, split, merge, or melt back to sats

Redeem notes with lnurl-wallet (hosted at wallet.lnurlcash.com) or any LUD-03/LUD-25 compatible wallet.

Features

  • Per-wallet mints — each LNbits wallet can create and configure its own mint with custom fees, limits, and identity
  • Mint (LUD-06) — pay an invoice, receive a bearer note keyed by the payment preimage
  • Melt (LUD-03) — redeem a note for a BOLT-11 payment, with confirm-before-burn tristate settlement
  • Rotate — burn a note, mint a fresh one keyed by a wallet-supplied hash (same value)
  • Split — burn one or more notes, mint two (amount + change)
  • Merge — burn several notes, mint one worth the sum
  • Verify (LUD-21) — settlement status endpoint for mint and melt invoices
  • Offline verification (LUD-25) — per-mint secp256k1 keypair; mintPubkey advertised, sig/sig2 on rotate/split/merge
  • Comment protection — notes can be keyed by a wallet-supplied comment hash instead of the payment preimage
  • Sunset mode — stop issuing new notes while allowing existing notes to be redeemed
  • Tor support — onion URL substitution for callback URLs
  • Mint fees — configurable base fee + percentage, withheld at mint time
  • Management SPA — create/configure mints, view outstanding notes and activity
  • Public one-pager — QR code, LNURL, mint limits, pubkey, and node info

Installation

From source (dev)

cd lnbits/lnbits/extensions
git clone https://github.com/bitkarrot/lnurlmint.git

Restart LNbits. The extension appears in the extensions list.

Requirements

  • LNbits >= 1.5.4
  • No additional Python dependencies — uses only libraries already in LNbits (bolt11, bech32, httpx, pyqrcode, loguru)

Usage

1. Create a mint

In the LNbits UI, open the lnurlmint extension and click Create Mint. Configure:

Field Description Default
Username Mint identity (shown on the public page)
Min sendable Minimum mintable amount (msat) 10,000 (10 sats)
Max sendable Maximum mintable amount (msat) 1,000,000,000 (1,000 sats)
Min mint Net-of-fee floor — rejects mints where amount - fee < min_mint 10,000
Base fee Flat fee per mint (msat) 0
Fee percent Percentage fee in ppm (parts per million) 0
Verify enabled Toggle LUD-21 verify endpoint true
Sunset mint Stop issuing new notes false
Onion URL Tor hidden service URL for callback substitution

2. Share the LNURL

Each mint has a public one-pager at /lnurlmint/m/{mint_id} showing a QR code of the mint's LNURL. Share this URL or the LNURL string with anyone who should be able to mint notes.

3. Mint a note

A payer scans the QR code (or pastes the LNURL into a wallet), pays the invoice, and receives a bearer note. The payment preimage becomes the note's secret (k1).

4. Redeem a note

The note holder opens the lnurlw:// link in a compatible wallet and chooses:

  • Melt — provide a BOLT-11 invoice; the note is reserved, the invoice is paid asynchronously, and the note is burned on settlement
  • Rotate — get a fresh note (same value) keyed by a new hash
  • Split — break into two notes (specified amount + change)
  • Merge — combine multiple notes into one

API Reference

Management API (admin key)

Method Endpoint Description
POST /lnurlmint/api/v1/mints Create a mint
GET /lnurlmint/api/v1/mints List mints for wallet
GET /lnurlmint/api/v1/mints/{mint_id} Get mint details
PUT /lnurlmint/api/v1/mints/{mint_id} Update mint config
DELETE /lnurlmint/api/v1/mints/{mint_id} Delete mint (fails if notes outstanding)
GET /lnurlmint/api/v1/mints/{mint_id}/notes List outstanding notes
GET /lnurlmint/api/v1/mints/{mint_id}/activity Recent mint/melt activity

Public API (no auth)

Method Endpoint Description
GET /lnurlmint/api/v1/public/{mint_id} Mint info for the public one-pager

LNURL endpoints (no auth)

Method Endpoint LUD Description
GET /lnurlmint/lnurlp/{mint_id} LUD-06 payRequest with withdrawLink advertisement
GET /lnurlmint/p/cb/{mint_id} LUD-06 Pay callback — returns invoice, preimage becomes note
GET /lnurlmint/w/{mint_id} LUD-03 withdrawRequest (informational, never burns)
GET /lnurlmint/w/cb/{mint_id} LUD-03 Mutating callback — melt, rotate, split, merge
GET /lnurlmint/verify/{mint_id}/{payment_hash} LUD-21 Settlement status for mint/melt invoices

Withdraw callback semantics (/w/cb/{mint_id})

k1 pr amount Result
one yes Melt: note reserved, invoice paid async, burned on settlement
one no no Rotate: burned, new note keyed by h (same value)
one+ no yes Split: all burned, two notes minted (amounth, remainder → h2)
many no Merge: all burned, one note worth the sum keyed by h

h (sha256 of the new note's preimage) is required whenever pr is absent. h2 is additionally required for splits. The wallet — never the mint — generates the replacement note's secret.

Security

  • Confirm-before-burn — melting a note reserves it (pending), pays the invoice asynchronously, and only burns on positive settlement. A failed payment restores the note.
  • No double-spend — pending notes reject all callbacks with {"status":"ERROR","reason":"pending"}
  • No secret leakage — the mint stores sha256(k1) (the hash), never the preimage itself. For rotate/split/merge, the mint never sees the new note's secret — only the wallet-supplied hash.
  • Per-wallet isolation — every query is scoped by wallet_id; no cross-wallet note access is possible
  • Background reconciliation — a permanent task checks in-flight melts and resolves them (settle or restore) even if payment hooks are missed

Testing

cd lnbits
.venv/bin/python -m pytest lnbits/extensions/lnurlmint/tests/ -v

The test suite includes 167 test functions across 21 files, ported from the original lnurl-mint project — covering double-spend, race conditions, preimage leakage, fee conservation, reconcile-inflight, bearer threat scenarios, and all LNURL endpoint behavior.

Architecture

lnurlmint/
├── __init__.py          # Extension registration, lifecycle, static files
├── config.py            # Extension-level settings (env vars)
├── models.py            # Pydantic v1 models (Mint, Note, wire models)
├── migrations.py        # DB migrations (mints, notes, mints_records, melts)
├── crud.py              # Wallet-scoped DB operations
├── services.py          # Business logic (mint, melt, rotate, split, merge, reconcile)
├── signing.py           # Per-mint secp256k1 keypair, sign_note
├── tasks.py             # Background reconcile task
├── views.py             # Generic routes (management page)
├── views_api.py         # Management + public REST API
├── views_lnurl.py       # LNURL endpoints (payRequest, withdraw, verify)
├── manifest.json        # LNbits extension manifest
├── config.json          # LNbits extension config
├── static/
│   ├── js/
│   │   ├── index.js     # Management SPA (Vue 3, inline template)
│   │   ├── index.vue    # Dummy template (required by LNbits loader)
│   │   ├── public.js    # Public one-pager (Vue 3, inline template)
│   │   └── public.vue   # Dummy template
│   └── routes.json      # SPA route definitions
└── tests/               # 167 test functions, 21 files

Differences from standalone lnurl-mint

Aspect lnurl-mint (standalone) lnurlmint (LNbits extension)
Funding Direct lnd/cln REST client LNbits wallet abstraction (create_invoice/pay_invoice)
Multi-tenancy Single global mint Per-wallet mints
Database Standalone sqlite + module-level lock LNbits Database (SQLite + Postgres)
Lightning Address .well-known/lnurlp/{username} (LUD-16) Raw LNURL/QR (LUD-16 deferred to v2)
Frontend Server-rendered HTML page Vue 3 SPA (management + public)
Background tasks FastAPI lifespan + monitor LNbits create_permanent_unique_task
Offline verification Node-direct signmessage (Option A) Per-mint keypair (Option B, portable)

Credits

  • dni — original lnurl-mint implementation
  • bitkarrot — LNbits extension port

License

MIT

About

lnurl mint extension for lnbits

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages