No description
  • Python 54.7%
  • HTML 27.8%
  • JavaScript 16.4%
  • Makefile 1.1%
Find a file
Patrick Mulligan 6893f409be
Some checks failed
lint.yml / feat(lnurl): access-control verify endpoint (push) Failing after 0s
feat(lnurl): access-control verify endpoint
GET /api/v1/verify/{external_id}?p=&c= — side-effect-light SUN check for
access control (doors via the aiolabs  extension): confirms a genuine,
non-replayed tap and returns the card identity (external_id, card_name),
WITHOUT /scan's spend semantics (no withdrawRequest, no daily-limit, no hit).
It still advances the SUN counter, so a captured p/c can't be replayed.

Named 'verify' because /auth is already the card-programming OTP endpoint.
config.json → 1.1.1-aio.2.
2026-08-07 21:52:31 +02:00
.github/workflows feat: code quality (#39) 2024-08-29 12:24:15 +02:00
static feat: deeplinks support (#58) 2025-06-16 14:17:08 +01:00
templates/boltcards feat: use lnurl lib (#60) 2025-09-09 10:53:56 +02:00
tests feat: code quality (#39) 2024-08-29 12:24:15 +02:00
.gitignore feat: code quality (#39) 2024-08-29 12:24:15 +02:00
.prettierrc feat: code quality (#39) 2024-08-29 12:24:15 +02:00
__init__.py feat: use uv (#59) 2025-09-04 07:57:33 +02:00
config.json feat(lnurl): access-control verify endpoint 2026-08-07 21:52:31 +02:00
crud.py feat: use uv (#59) 2025-09-04 07:57:33 +02:00
description.md feat: code quality (#39) 2024-08-29 12:24:15 +02:00
LICENSE add license 2023-02-24 18:13:38 +01:00
Makefile feat: use uv (#59) 2025-09-04 07:57:33 +02:00
manifest.json Create manifest.json 2023-02-14 11:08:40 +00:00
migrations.py fix: wrong typing in DB columns (#51) 2025-04-15 12:29:34 +01:00
models.py fix: models for deeplink (#63) 2025-12-16 10:57:11 +00:00
nxp424.py feat: code quality (#39) 2024-08-29 12:24:15 +02:00
package-lock.json feat: code quality (#39) 2024-08-29 12:24:15 +02:00
package.json feat: code quality (#39) 2024-08-29 12:24:15 +02:00
pyproject.toml feat: use uv (#59) 2025-09-04 07:57:33 +02:00
README.md feat(lnurl): tap-to-receive (top-up) endpoint 2026-08-06 00:52:46 +02:00
tasks.py feat: update to lnbits 1.0.0 (#41) 2024-10-23 15:45:23 +02:00
toc.md feat: code quality (#39) 2024-08-29 12:24:15 +02:00
uv.lock feat: use uv (#59) 2025-09-04 07:57:33 +02:00
views.py feat: use uv (#59) 2025-09-04 07:57:33 +02:00
views_api.py feat: use lnurl lib (#60) 2025-09-09 10:53:56 +02:00
views_lnurl.py feat(lnurl): access-control verify endpoint 2026-08-07 21:52:31 +02:00

Bolt Cards - LNbits extension

For more about LNBits extensions check this tutorial

This extension allows you to link your Bolt Card on a NXP NTAG424 DNA tag with a LNbits hub that generated new links on each tab which allows a better privacy and security than a static LNURLw that you can also write to a NFC tag (fromon NTAG 213) in the withdraw-extension e.g. for one-time usage as a gift-card.

Video Tutorial

Disclaimer: Use this only if you either know what you are doing or are a reckless lightning pioneer. Only you are responsible for all your sats, cards and other devices. Always backup all your card keys!

For the easy way you need:

  • an LNbits instance in clearnet
  • opened on Android in Chrome browser
  • Boltcard extension installed for your LNbits wallet
  • Boltcard NFC Card Creator App from the Apple- or Play-Store to write your keys to the tags once they were generated on LNbits

If you want to gift a Boltcard, make sure to include the following data in your present, so that the user is able to make full use of it.

Always backup all keys that you're trying to write on the card. Without them you may not be able to change them in the future!

Setting the card - Boltcard NFC Card Creator (easy way)

  • Add new card in the extension.
    • Set a max sats per transaction. Any transaction greater than this amount will be rejected. This is usually set higher than the funds in the wallet are to prevent accidential withdraws.
    • Set a max sats per day. After the card spends this amount of sats in a day, additional transactions will be rejected.
    • Set a card name. This is just for your reference inside LNbits.
    • Set the card UID. This is the unique identifier of your NFC card and is 7 bytes.
      • If on an Android device with a newish version of Chrome, you can click the icon next to the input and tap your card to autofill this field.
      • Otherwise read it with the Bolt-Card app (Read NFC) and paste it to the field.
    • Advanced Options
      • Card Keys (k0, k1, k2) will be automatically generated if not explicitly set.
        • Set to 16 bytes of 0s (00000000000000000000000000000000) to leave the keys in default (empty) state (this is unsecure).
        • GENERATE KEY button fill the keys randomly.
    • Click CREATE CARD button
  • Click the QR code button next to a card to view its details. Backup the keys now! They'll be comfortable in your password manager.
    • Now you can scan the QR code with the Boltcard app (Create Bolt Card -> SCAN QR CODE).
    • Or the "KEYS / AUTH LINK" button to copy the auth URL to the clipboard. Then paste it into the Android app (Create Bolt Card -> PASTE AUTH URL).
  • Click WRITE CARD NOW and approach the NFC card to set it up. DO NOT REMOVE THE CARD PREMATURELY!

Erasing the card - Boltcard NFC Card Creator

Updated for v0.1.9

Since v0.1.2 of Boltcard NFC Card Creator it is possible not only to reset the keys but also to disable the SUN function and do the complete erase so the card can be used again as a static tag (or set as a new Bolt Card, ofc).

  • In the Boltcard extension click the QR code button next to a card to view its details and select WIPE
  • OR click the red cross icon on the right side to reach the same
  • In the Boltcard app (Reset Keys)
    • Click SCAN QR CODE to scan the QR
    • Or click WIPE DATA in LNbits to copy and paste in to the app (PASTE KEY JSON)
  • Click RESET CARD NOW and approach the NFC card to erase it. DO NOT REMOVE THE CARD PREMATURELY!
  • Now if all is successful the card can be safely deleted from LNbits (but keep the keys backuped anyway; batter safe than brick).

If you somehow find yourself in some non-standard state (for instance only k3 and k4 remains filled after previous unsuccessful reset), then you need to edit the key fields manually (for instance leave k0-k2 to zeroes and provide the right k3 and k4).

Setting the card (advanced)

A technology called Secure Unique NFC is utilized in this workflow.

About the keys

Up to five 16-byte keys can be stored on the card, numbered from 00 to 04. In the empty state they all should be set to zeros (00000000000000000000000000000000). For this extension only two keys need to be set, but for the security reasons all five keys should be changed from default (empty) state. The keys directly needed by this extension are:

  • One for encrypting the card UID and the counter (p parameter), let's called it meta key, key #01 or K1.

  • One for calculating CMAC (c parameter), let's called it file key, key #02 or K2.

The key #00, K0 (also know as auth key) is used as authentification key. It is not directly needed by this extension, but should be filled in order to write the keys in cooperation with Boltcard NFC Card Creator. In this case also K3 is set to same value as K1 and K4 as K2, so all keys are changed from default values. Keep that in your mind in case you ever need to reset the keys manually.

The writing process

There's also a more advanced guide to set cards up manually with a card reader connected to your computer. Writing can also be done (without setting the keys) via the TagWriter app by NXP on Android.

The URI should be lnurlw://YOUR_LNBITS_DOMAIN/boltcards/api/v1/scan/{YOUR_card_external_id}?p=00000000000000000000000000000000&c=0000000000000000

Then fill up the card parameters in the extension. Card Auth key (K0) can be filled in the extension just for the record. Initical counter can be 0.

  • If you don't know the card ID, use NXP TagInfo app to read it first.
  • Tap Write tags > New Data Set > Link
  • Set URI type to Custom URL
  • URL should look like lnurlw://YOUR_LNBITS_DOMAIN/boltcards/api/v1/scan/{YOUR_card_external_id}?p=00000000000000000000000000000000&c=0000000000000000
  • click Configure mirroring options
  • Select Card Type NTAG 424 DNA
  • Check Enable SDM Mirroring
  • Select SDM Meta Read Access Right to 01
  • Check Enable UID Mirroring
  • Check Enable Counter Mirroring
  • Set SDM Counter Retrieval Key to 0E
  • Set PICC Data Offset to immediately after e=
  • Set Derivation Key for CMAC Calculation to 00
  • Set SDM MAC Input Offset to immediately after c=
  • Set SDM MAC Offset to immediately after c=
  • Save & Write
  • Scan with compatible Wallet

This app afaik cannot change the keys. If you cannot change them any other way, leave them empty in the extension dialog and remember you're not secured. Card Auth key (K0) can be omitted anyway. Initical counter can be 0.


aiolabs fork — tap-to-receive (top-up)

This fork adds a deposit counterpart to the /scan withdraw, so a Bolt Card can be tapped to receive sats (e.g. the buy flow on a bitSpire ATM), not only to spend.

A Bolt Card only ever emits its lnurlw (a spend voucher), so the tap is used purely as an authenticated identity: the same NTAG424 SUN p/c that /scan verifies proves card possession, and the endpoint returns an lnurl-pay (LUD-06) response for the card's own wallet instead of a withdraw voucher. No card re-writing — same NDEF, keys, and external_id.

Endpoint (sibling of /scan):

GET /boltcards/api/v1/pay/{external_id}?p=<32-hex>&c=<16-hex>
  → LnurlPayResponse { tag:"payRequest", callback, minSendable, maxSendable, metadata }
GET /boltcards/api/v1/pay/cb/{hit_id}?amount=<msat>
  → LnurlPayActionResponse { pr:<bolt11 on the card wallet> }
  • SUN verification, counter monotonicity, and the single-use hit bearer token mirror /scan exactly (hit_id bridges the two LUD-06 steps like k1 does for withdraw). A cloned UID can't misdirect a deposit.
  • No daily-limit check (that gates spending); per-deposit max is tx_limit.
  • Distinct from the existing LUD-19 refund lnurlp (which is keyed by a prior scan's hit); this is reachable directly by a tap via external_id.