Wrapper for Lightning Network Daemon (lnd) ⚡
It provides separate accounts for end users. Live deployment at ln.getalby.com. API documentation
LndHub compatible API implemented in Go using relational database backends
- Using a relational database (PostgreSQL)
- Focussing only on Lightning (no onchain functionality)
- No runtime dependencies (simple Go executable)
- Extensible to add more features
All required configuration is done with environment variables and a .env
file can be used.
Check the .env_example
for an example.
cp .env_example .env
vim .env # edit your config
DATABASE_URI
: The URI for the database. (eg.postgresql://user:password@localhost:5432/lndhub?sslmode=disable
)JWT_SECRET
: We use JWT for access tokens. Configure your secret hereJWT_ACCESS_EXPIRY
: How long the access tokens should be valid (in seconds, default 2 days)JWT_REFRESH_EXPIRY
: How long the refresh tokens should be valid (in seconds, default 7 days)LND_ADDRESS
: LND gRPC address (with port) (e.g.localhost:10009
)LND_MACAROON_HEX
: LND macaroon (hex-encoded contents ofadmin.macaroon
orlndhub.macaroon
, see below)LND_MACAROON_FILE
: LND macaroon (provided as path on a filesystem)LND_CERT_HEX
: LND certificate (hex-encoded contents oftls.cert
)LND_CERT_FILE
: LND certificate (provided as path on a filesystem)CUSTOM_NAME
: Name used to overwrite the node alias in the getInfo callLOG_FILE_PATH
: (optional) By default all logs are written to STDOUT. If you want to log to a file provide the log file path hereSENTRY_DSN
: (optional) Sentry DSN for exception trackingHOST
: (default: "localhost:3000") Host the app should listen onPORT
: (default: 3000) Port the app should listen onDEFAULT_RATE_LIMIT
: (default: 10) Requests per second rate limitSTRICT_RATE_LIMIT
: (default: 10) Requests per second rate limit for resource-intensive APIs (e.g. sending a payment)BURST_RATE_LIMIT
: (default: 1) Specifies the maximum number of requests that can pass at the same momentENABLE_PROMETHEUS
: (default: false) Enable Prometheus metrics to be exposedPROMETHEUS_PORT
: (default: 9092) Prometheus port (path:/metrics
)WEBHOOK_URL
: Optional. Callback URL for incoming and outgoing payment events, see below.FEE_RESERVE
: (default: false) Keep fee reserve for each userALLOW_ACCOUNT_CREATION
: (default: true) Enable creation of new accountsADMIN_TOKEN
: Only allow account creation requests if they have the headerAuthorization: Bearer ADMIN_TOKEN
. Also required for endpoint for updating users login, password and (de)activation status.MIN_PASSWORD_ENTROPY
: (default: 0 = disable check) Minimum entropy (bits) of a password to be accepted during account creationMAX_RECEIVE_AMOUNT
: (default: 0 = no limit) Set maximum amount (in satoshi) for which an invoice can be createdMAX_SEND_AMOUNT
: (default: 0 = no limit) Set maximum amount (in satoshi) of an invoice that can be paidMAX_ACCOUNT_BALANCE
: (default: 0 = no limit) Set maximum balance (in satoshi) for each accountMAX_SEND_VOLUME
: (default: 0 = no limit) Set maximum volume (in satoshi) for sending for each accountMAX_RECEIVE_VOLUME
: (default: 0 = no limit) Set maximum volume (in satoshi) for receiving for each accountMAX_VOLUME_PERIOD
: (default: 2592000 = 1 month) Rolling period over which the volume should be calculated in secondsSERVICE_FEE
: (default: 0 = no service fee) Set the service fee for each outgoing transaction in 1/1000 (e.g. 1 means a fee of 1sat for 1000sats - rounded up to the next bigger integer)NO_SERVICE_FEE_UP_TO_AMOUNT
(default: 0 = no free transactions) the amount in sats up to which no service fee should be charged
There are two ways how to obtain hex-encoded macaroon needed for LND_MACAROON_HEX
.
Either you hex-encode the admin.macaroon
:
xxd -p -c 1000 ~/.lnd/data/chain/bitcoin/mainnet/admin.macaroon
Or you bake a new macaroon with the following permissions and use that instead:
lncli bakemacaroon info:read invoices:read invoices:write offchain:read offchain:write
If you want to use a macaroon stored on a filesystem, you either set LND_MACAROON_FILE
to a path pointing to admin.macaroon
or use the following command to generate the lndhub.macaroon
and setting the variable to path of that file.
lncli bakemacaroon --save_to=lndhub.macaroon info:read invoices:read invoices:write offchain:read offchain:write
To run the server
go run cmd/server/main.go
To build an lndhub
executable, run the following commands:
make
To run your own local lightning network and LND you can use Lightning Polar which helps you to spin up local LND instances.
Alternatively you can also use the Alby simnetwork
LndHub.go requires a PostgreSQL database backend.
Prometheus metrics can be optionally exposed through the ENABLE_PROMETHEUS
environment variable.
For an example dashboard, see https://grafana.com/grafana/dashboards/10913.
If WEBHOOK_URL
is specified, a http POST request will be dispatched at that location when an incoming payment is settled, or an outgoing payment is completed. Example payload:
{
"id": 721,
"type": "incoming", //incoming, outgoing
"user_id": 299,
"amount": 1000,
"fee": 0,
"memo": "fill wallet",
"description_hash": "",
"payment_request": "lnbcrt10u1p38p4ehpp5xp07pda02vk40wxd9gyrene8qzheucz7ast435u9jwxejs6f0v5sdqjve5kcmpqwaskcmr9wscqzpgxqyz5vqsp56nyve3v5fw306j74nmewv7t5ey3aer2khjrrwznh4k2vuw44unzq9qyyssqv2wq9hn7a39x8cvz9fvpzul87u4kc4edf0t6jukzvmx8v5swl3jqg8p3sh6czkepczcjkm523q9x8yswsastctnsns3q9d26szu703gpwh7a09",
"destination_pubkey_hex": "0376442c750766d5d127512609a5618d9aa82db2d06aae226084da92a3e133acda",
"custom_records": {
"5482373484": "YWY4MDhlZDUxZjNmY2YxNWMxYWI3MmM3ODVhNWI1MDE="
}, //only set when keysend=true
"r_hash": "305fe0b7af532d57b8cd2a083ccf2700af9e605eec1758d385938d9943497b29",
"preimage": "3735363531303032626332356439376136643461326434336335626434653035",
"keysend": false,
"state": "settled",
"created_at": "2022-05-03T09:18:15.15774+02:00",
"expires_at": "2022-05-04T09:18:15.157597+02:00",
"updated_at": "2022-05-03T09:18:19.837567+02:00",
"settled_at": "2022-05-03T09:18:19+02:00"
}
Both incoming and outgoing keysend payments are supported. For outgoing keysend payments, check out the API documentation.
For incoming keysend payments, we are using a custom TLV record with type 696969
, which should contain the hex-encoded login
of the receiving user's account. TLV records are stored as json blobs with the invoices and are returned by the /getuserinvoices
endpoint.
The V2 API has an endpoint to make multiple keysend payments with 1 request, which can be useful for splitting value4value payments.
- Using low level database constraints to prevent data inconsistencies
- Follow double-entry bookkeeping ideas (Every transaction is a debit of one account and a credit to another one)
┌─────────────┐
│ User │
└─────────────┘
│
┌─────────────────┬───────┴─────────┬─────────────────┐
▼ ▼ ▼ ▼
Accounts: ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ Incoming │ │ Current │ │ Outgoing │ │ Fees │
Every user has └──────────────┘ └──────────────┘ └──────────────┘ └──────────────┘
four accounts
Every Transaction Entry is associated to one debit account and one
credit account
┌────────────────────────┐
│Transaction Entry │
│ │
│+ user_id │
┌────────────┐ │+ invoice_id │
│ Invoice │────────────────▶+ debit_account_id │
└────────────┘ │+ credit_account_id │
│+ amount │
Invoice holds the │+ ... │
lightning related │ │
data └────────────────────────┘