lnlink-server
v1.1.6
Published
This project is an SDK library developed based on the Nostr Asset Protocol, designed to facilitate interactions with Lightning Network Daemon (LND) and RGB nodes, enabling Nostr-based functionalities.
Readme
LNLink
This project is an SDK library developed based on the Nostr Asset Protocol, designed to facilitate interactions with Lightning Network Daemon (LND) and RGB nodes, enabling Nostr-based functionalities.
Prerequisites & Installation
- Node.js >= 19
- Yarn >= 1.22
Install project dependencies once at the repository root:
yarn installNotes:
- Native deps such as
@prisma/clientare required at runtime and are not bundled by esbuild. - Binaries (
litd,rgb-lightning-node,tor) are prepared byscripts/binSetup.json demand.
Getting Started
There are three ways to start the project. Choose the one that fits your scenario.
1) Quick Start with Local Binaries
This mode runs everything on your host using local binaries. It is the fastest way to try the project.
Prerequisites
- Node.js >= 19
- The project will download platform-specific binaries automatically on first run.
Prepare env file at project root:
.env.bin- Common variables:
LINK_BINARY_PATH=/<Your path>/binor an absolute path, e.g./root/data/binLINK_NAME=...LINK_DATA_PATH=/root/data/<name>or an absolute pathLINK_HTTP_PORT=8099LINK_ENABLE_TOR=false(optional)LINK_OWNER: Nostr npub of the owner/operatorLINK_NETWORK: Bitcoin network (regtest,testnet,mainnet)
- Common variables:
Install deps (first time only)
yarn installStart
yarn start:binWhat happens:
scripts/binSetup.jsruns first to ensure required binaries exist (downloads usingbinaries.json).- The app then starts with variables loaded from
.env.bin.
2) Development Mode (Docker Compose)
Use this for local development and debugging with Docker.
Prepare env file at project root:
.env.devEnvironment variables overview (
.env.dev):LINK_NAME: Logical name of this node instance. Used to name Docker volumes and service identifiers.LINK_DATA_PATH: Absolute path to persist data (wallet, db, macaroons, etc.). Prefer an absolute path to avoid ambiguity.LINK_OWNER: Nostr npub of the owner/operator.LINK_NETWORK: Bitcoin network. Typical values:regtest,testnet,mainnet.LINK_ENABLE_TOR: Whether to enable Tor support (true/false).LINK_HTTP_PORT: API HTTP port exposed by this wrapper inside Docker to your host.LINK_LND_RPC_PORT: LND gRPC port exposed to the host.LINK_RGB_LISTENING_PORT: RGB service port exposed to the host.LINK_NODE_ENV: Node.js runtime environment, typicallydevelopmentfor this mode.LINK_NOSTR_NODE_NPUBKEY: The npub (hex-encoded pubkey) of the Nostr relay/node this instance peers with.LINK_NOSTR_NODE_HOST: The Nostr Lightning node host (pubkey@host:port or host:port) used for peering.LINK_ORACLE_SERVER_HOST: Oracle/bridge service host for price or external queries if applicable.LINK_BITCOIND_RPCHOST: Bitcoind RPC host (host:port) that LND connects to (for regtest/testnet/mainnet as configured).LINK_BITCOIND_RPCUSER: Bitcoind RPC username.LINK_BITCOIND_RPCPASS: Bitcoind RPC password.LINK_BITCOIND_ZMQBLOCK: Bitcoind ZMQ block notifications endpoint.LINK_BITCOIND_ZMQRAWTX: Bitcoind ZMQ raw transaction notifications endpoint.
Example
.env.dev(regtest):LINK_NAME=<Your name> LINK_DATA_PATH='/<Your project path>/docker/volumes/${LINK_NAME}' LINK_DATABASE_URL=file:/<Your project path>/docker/volumes/${LINK_NAME}/link/lnlink.db LINK_OWNER=npub1q7amuklx0fjw76dtulzzhhjmff8du5lyngw377d89hhrmj49w48ssltn7y LINK_NETWORK=regtest LINK_ENABLE_TOR=false LINK_HTTP_PORT=8090 LINK_LND_RPC_PORT=30009 LINK_RGB_LISTENING_PORT=5002 LINK_NODE_ENV=development LINK_NOSTR_NODE_NPUBKEY=027d2f1be71dc24c60b15070489d4ef274dd6aac236d02c67c76d6935defba56a6 LINK_NOSTR_NODE_HOST=regtest.lnfi.network:9735 LINK_ORACLE_SERVER_HOST=grpc-oracle.lnfi.network:443 LINK_BITCOIND_RPCHOST=regtest.lnfi.network:18443 LINK_BITCOIND_RPCUSER=lnfi_user LINK_BITCOIND_RPCPASS=lnfi_pass12GA LINK_BITCOIND_ZMQBLOCK=tcp://regtest.lnfi.network:28334 LINK_BITCOIND_ZMQRAWTX=tcp://regtest.lnfi.network:28335Start
yarn start:docker:devThis will:
- Use
.env.devfor environment variables. - Launch services defined in
docker-compose.dev.yml.
- Use
Run steps (recommended workflow):
- Start dependencies (Docker services) in Terminal A:
yarn start:docker:dev - Start the application in Terminal B (local Node process using
.env.dev):yarn start:dev
Stop / cleanup:
- Stop the application: Ctrl+C in the Terminal B running
yarn start:dev. - Stop Docker services:
docker compose --env-file ./.env.dev -f ./docker-compose.dev.yml down
Network prerequisite:
The compose file uses an external Docker network named
lnfi_network. Create it once if it doesn't exist:docker network create lnfi_networkTips:
- Ensure
LINK_DATA_PATHexists and is writable by Docker; it will persist LND/RGB data and macaroons. - Make sure the specified ports (
LINK_HTTP_PORT,LINK_LND_RPC_PORT,LINK_RGB_LISTENING_PORT) are free on your host. - For
regtest, confirm yourBITCOIND_*endpoints are reachable from the Docker network.
- Ensure
3) Docker Compose (Standard, Multi-Network)
Use this when you want to run LN-Link fully inside Docker with a clear separation between
environment settings (.env.*) and network profiles (DB settings).
There are three environment files for different networks:
.env.regtest.env.testnet.env.mainnet(optional, usually only contains non-sensitive runtime values)
Each file typically contains:
LINK_NAME: Logical name of this node instance (used for container/alias/naming).LINK_NETWORK: Bitcoin network (regtest,testnet,mainnet).LINK_DATA_PATH: Data persistence path (relative path supported, e.g../docker/volumes/linkdev).LINK_OWNER: Nostr npub of the owner/operator.LINK_ENABLE_TOR: Enable Tor support (true/false).LINK_RGB_LDK_PEER_LISTENING_PORT: RGB LDK peer listening port (e.g.9750).LINK_RGB_HOST: Host advertised for RGB peers (e.g. host LAN IP in regtest).LINK_REPORT_BASE_URL,LINK_REPORT_ADDRESS: Optional reporting/telemetry endpoints.
Example .env.regtest:
LINK_NAME=linkdev
LINK_NETWORK=regtest
LINK_NODE_ENV=production
LINK_DATA_PATH=./docker/volumes/linkdev
LINK_ENABLE_TOR=false
LINK_OWNER=npub1...
LINK_REPORT_BASE_URL=https://devoffaucet.unift.xyz
LINK_REPORT_ADDRESS=npub1...
LINK_RGB_LDK_PEER_LISTENING_PORT=9750
LINK_RGB_HOST=192.168.0.117Example .env.testnet is similar but typically uses a different LINK_DATA_PATH and
possibly different ports/host IP.
Start (regtest)
yarn start:regtestStart (testnet)
yarn start:testnetStart (mainnet)
# Requires a proper .env.mainnet and network settings (see LINK_SETTINGS_PATH below) yarn start:mainnet
Each command will:
- Use the corresponding
.env.<network>file viadocker-compose-lnlink.yml. - Launch the
litservice (LN-Link + litd + RGB) inside Docker on the external networklnfi_network.
4) Run from dist (bundled output)
Build first:
yarn buildRun using local binaries and env .env.bin (requires bin/ and root node_modules/):
yarn start:bin:distNotes about dist/ runtime:
dist/index.jsstill requires runtime dependencies from the repository rootnode_modules/(e.g.,@grpc/grpc-js,@prisma/client).- The
proto/folder is copied todist/proto/during build; code resolves proto paths viaprocess.cwd(). - The
bin/folder (withlitd,rgb-lightning-node,tor) is required when running with local binaries; prepare it usingyarn start:binonce, or runnode ./scripts/binSetup.js.
Using as NPM Package
After building with yarn build, the dist/ directory contains a complete NPM package that can be published and used in other projects.
Installation
npm install @lnfi-network/ln-linkNote: Database initialization (Prisma client generation and migrations) is handled at runtime to avoid requiring environment variables during package installation.
Usage in Node.js Applications
// Import the main application entry point
require('@lnfi-network/ln-link');
// The application will start automatically when imported
// Configuration is handled via environment variables or config filesFor programmatic control in Node.js:
const path = require('path');
// Set environment variables before importing
process.env.LINK_DATA_PATH = path.join(__dirname, 'ln-link-data');
process.env.LINK_NETWORK = 'testnet';
process.env.LINK_HTTP_PORT = '8099';
process.env.LINK_OWNER = 'npub1...'; // Your Nostr public key
process.env.LINK_DATABASE_URL = 'file:' + path.join(__dirname, 'ln-link-data', 'lnlink.db');
// You may need to run Prisma setup manually for Node.js applications:
// npx prisma generate
// npx prisma migrate deploy
// Import and start the application
require('@lnfi-network/ln-link');Usage in Electron Applications
The package provides a dedicated Electron entry point for desktop applications:
// In your Electron main process
const { app, BrowserWindow } = require('electron');
const path = require('path');
// Import the Electron-specific entry point
const LnLinkElectron = require('@lnfi-network/ln-link/electron');
// Global LN-Link instance
let lnLink = null;
app.whenReady().then(async () => {
// Create your Electron window
const mainWindow = new BrowserWindow({
width: 1200,
height: 800,
webPreferences: {
nodeIntegration: true,
contextIsolation: false
}
});
// Create LN-Link instance
lnLink = new LnLinkElectron({
dataPath: path.join(app.getPath('userData'), 'ln-link'),
network: 'testnet', // or 'mainnet', 'regtest'
httpPort: 8099,
name: 'my-electron-app',
enableTor: false,
owner: 'npub1...', // Your Nostr public key
debug: true,
// binaryPath: '/path/to/binaries', // Optional: for local binary mode
// Database setup is handled automatically in Electron applications
});
await lnLink.start();
// Load your application UI
mainWindow.loadFile('index.html');
});
app.on('window-all-closed', async () => {
// Gracefully shutdown LN-Link
if (lnLink) {
await lnLink.stop();
}
if (process.platform !== 'darwin') {
app.quit();
}
});Configuration Options
When using the package in other applications, you can configure it programmatically:
const config = {
// Data storage path
dataPath: '/path/to/data',
// Network configuration
network: 'testnet', // 'mainnet', 'testnet', 'regtest'
// Application name
name: 'my-app',
// Service ports
httpPort: 8099,
lndRpcPort: 10009,
rgbPort: 5002,
// Nostr configuration
owner: 'npub1...', // Your Nostr public key
// Optional features
enableTor: false,
debug: false,
// Binary paths (for local binary mode)
binaryPath: '/path/to/binaries',
// Database configuration (optional - defaults to dataPath/lnlink.db)
databaseUrl: 'file:/path/to/custom/database.db',
};
## Configuration Overview
### DB-backed network settings (LnlinkConfig.settings)
LN-Link stores the **network profile** (bitcoind, RGB, Nostr, oracle, etc.) in the
`LnlinkConfig.settings` field in the database. At runtime, `getConfig()` merges:
- `config.default.js` (built-in defaults)
- Process env (`.env.*` / programmatic options)
- `LnlinkConfig.settings` (network profile)
For Docker/standalone modes (**`LINK_NODE_ENV !== "app"`**):
- On first start, if the DB has no `settings`, LN-Link will initialize them by:
1. If `LINK_SETTINGS_PATH` is set and points to a valid JSON file, load that JSON
and write it to `LnlinkConfig.settings`.
2. Otherwise, fall back to network templates:
- `setting.regtest.json` for `LINK_NETWORK=regtest`
- `setting.testnet.json` for `LINK_NETWORK=testnet`
- `setting.mainnet.json` for `LINK_NETWORK=mainnet` (template only, no secrets).
For security-sensitive environments (especially **mainnet**):
- Keep `setting.mainnet.json` as a **template only** (with placeholder values).
- Put the real mainnet JSON under a secure path on the server, e.g.:
- `/data/config/lnlink-mainnet-settings.json`
- Set:
```env
LINK_NETWORK=mainnet
LINK_SETTINGS_PATH=/data/config/lnlink-mainnet-settings.jsonso that first start reads this file and writes it into LnlinkConfig.settings.
Node name (LINK_NAME) and database node_name
The human-readable node name comes from LnlinkConfig.node_name and is exposed as
LINK_NAME by getConfig() with the following precedence:
- DB
node_name> envLINK_NAME>config.default.js.
On first initialization in Docker/standalone mode:
- LN-Link uses the current
LINK_NAMEto populateLnlinkConfig.node_name. - If
settingsalready exist butnode_nameis empty, it will backfillnode_nameonce from the currentLINK_NAME.
To change the node name later without touching env:
- Use the HTTP API:
This updatesPOST /api/lnd/update-name { "nodeName": "my-new-node-name" }LnlinkConfig.node_nameand triggersreloadConfig(), so subsequentgetConfig()calls see the newLINK_NAMEimmediately.
Notes:
- LND (litd) reads the alias/TLS extra domains only at process start
(via
buildLitdArgs). Changing the name via API does not automatically restart litd; restart is only needed if you want the alias/TLS SANs to match the new name. - RGB uses
LINK_NAMEasannounce_aliasduringunlockNode. After changing the name, the next unlock call will use the new alias; no process restart is required for RGB.
Electron mode (LINK_NODE_ENV = "app")
When running via the Electron entry point (lnlink.js / LnLinkElectron):
LINK_NODE_ENVis effectively"app".initLinkConfig()does not auto-initialize DB fromsetting.*.jsonorLINK_SETTINGS_PATH.- You are expected to initialize and update
LnlinkConfig.settingsandnode_namevia the HTTP APIs:POST /api/lnd/initwith{ owner, settings, nodeName }.POST /api/lnd/update-namewith{ nodeName }.
### Platform-Specific Notes
- **Prisma**: Database client generation and migrations are handled at runtime. For Node.js applications, you may need to run manually:
```bash
npx prisma generate # Generate client code
npx prisma migrate deploy # Apply database migrationsFor Electron applications, database setup is handled automatically.
- Binary Dependencies: When using local binaries (LND, RGB node, Tor), ensure they are available for your target platform. The package includes platform detection and automatic binary management.
Notes
- Local binary workflow
- The binary download/prepare workflow is implemented in
scripts/binSetup.js(callsscripts/binManage.js). - Download sources are configured in
binaries.jsonper-platform (darwin-arm64,darwin-x64,linux-x64). - Binaries are stored under
./binby default; you can override viaLINK_BINARY_PATH.
- The binary download/prepare workflow is implemented in
- Job system
- The system initializes with state-driven tasks; no manual
startJobsevents are needed.
- The system initializes with state-driven tasks; no manual
- Error handling
- Improved error typing in
business/job/core/StateManager.jsfor better diagnostics.
- Improved error typing in
Key Features
- SDK Library based on Nostr Asset Protocol.
- Integration with LND and RGB nodes.
- Enables Nostr-based functionalities for Lightning and RGB assets.
