SkillAgentSearch skills...

Lni

LNI Remote - Lightning Node Interface. A standard interface to remote connect to CLN, LND, Phoenixd, LNURL, BOLT 11, BOLT 12, NWC, Spark, Strike, Blink, and Speed. Language Binding support for kotlin, swift, react-native, nodejs (typescript, javaScript).

Install / Use

/learn @lightning-node-interface/Lni
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

LNI Remote - Lightning Node Interface Remote

Remote connect to all the major lightning node implementations with a standard interface.

  • Supports all major nodes - CLN, LND, Phoenixd, *LNDK (WIP)
  • Supports the main protocols - BOLT 11, BOLT 12, and NWC
  • LNURL & Lightning Address support - Pay to user@domain.com or lnurl1...
  • Also popular REST APIs (Custodial) - Strike, Speed, Blink
  • Language Binding support for kotlin, swift, react-native, nodejs (typescript/javascript). No support for WASM (yet)
  • Tor support
  • Runs on Android, iOS, Linux, Windows and Mac
<img src="./assets/logo.jpg" alt="logo" style="max-height: 300px;">

Interface API Examples

Rust

let lnd_node = LndNode::new(LndConfig { url, macaroon });
let cln_node = ClnNode::new(ClnConfig { url, rune });

let lnd_node_info = lnd_node.get_info();
let cln_node_info = cln_node.get_info();

let invoice_params = CreateInvoiceParams {
    invoice_type: InvoiceType::Bolt11,
    amount_msats: Some(2000),
    description: Some("your memo"),
    expiry: Some(1743355716),
    ..Default::default()
});

let lnd_invoice = lnd_node.create_invoice(invoice_params).await;
let cln_invoice = cln_node.create_invoice(invoice_params).await;

let pay_invoice_params = PayInvoiceParams{
    invoice: "{lnbc1***}", // BOLT 11 payment request
    fee_limit_percentage: Some(1.0), // 1% fee limit
    allow_self_payment: Some(true), // This setting works with LND, but is simply ignored for CLN etc...
    ..Default::default(),
});

let lnd_pay_invoice = lnd_node.pay_invoice(pay_invoice_params);
let cln_pay_invoice = cln_node.pay_invoice(pay_invoice_params);

let lnd_invoice_status = lnd_node.lookup_invoice("{PAYMENT_HASH}");
let cln_invoice_status = cln_node.lookup_invoice("{PAYMENT_HASH}");

let list_txn_params = ListTransactionsParams {
    from: 0,
    limit: 10,
    payment_hash: None, // Optionally pass in the payment hash, or None to search all
};

let lnd_txns = lnd_node.list_transactions(list_txn_params).await;
let cln_txns = cln_node.list_transactions(list_txn_params).await;

// See the tests for more examples
// LND - https://github.com/lightning-node-interface/lni/blob/master/crates/lni/lnd/lib.rs#L96
// CLN - https://github.com/lightning-node-interface/lni/blob/master/crates/lni/cln/lib.rs#L113
// Phoenixd - https://github.com/lightning-node-interface/lni/blob/master/crates/lni/phoenixd/lib.rs#L100

Typescript

import { createNode, InvoiceType, type BackendNodeConfig } from '@sunnyln/lni';

const backend: BackendNodeConfig = {
    kind: 'lnd',
    config: {
        url: 'https://lnd.example.com',
        macaroon: '...',
    },
};

const node = createNode(backend);
const nodeInfo = await node.getInfo();

const invoiceParams = {
    invoiceType: InvoiceType.Bolt11,
    amountMsats: 2000,
    description: "your memo",
    expiry: 1743355716,
};

const invoice = await node.createInvoice(invoiceParams);

const payInvoiceParams = {
    invoice: invoice.invoice, // BOLT 11 payment request
    feeLimitPercentage: 1, // 1% fee limit
    allowSelfPayment: true, // Used by LND; ignored by nodes that do not support it
};

const payInvoice = await node.payInvoice(payInvoiceParams);

const invoiceStatus = await node.lookupInvoice({ paymentHash: invoice.paymentHash });

const listTxnParams = {
    from: 0,
    limit: 10,
    paymentHash: undefined, // Optionally pass a payment hash to filter
};

const txns = await node.listTransactions(listTxnParams);

Payments

// BOLT 11
node.create_invoice(CreateInvoiceParams) -> Result<Transaction, ApiError>
node.pay_invoice(PayInvoiceParams) -> Result<PayInvoiceResponse, ApiError>

// BOLT 12
node.create_offer(params: CreateOfferParams)  -> Result<Offer, ApiError> 

node.get_offer(search: Option<String>) -> Result<Offer, ApiError> // return the first offer or by search id
node.pay_offer(offer: String, amount_msats: i64, payer_note: Option<String>) -> Result<PayInvoiceResponse, ApiError> 
node.list_offers(search: Option<String>) -> Result<Vec<Offer>, ApiError>

// LNURL & Lightning Address (see lnurl module)
lnurl::resolve_to_bolt11(destination, amount_msats) -> Result<String, ApiError>  // Resolve any destination to BOLT11
lnurl::get_payment_info(destination, amount_msats) -> Result<PaymentInfo, ApiError>  // Get min/max amounts
lnurl::detect_payment_type(destination) -> PaymentDestination  // Auto-detect: bolt11|bolt12|lnurl|lightning_address
lnurl::needs_resolution(destination) -> bool  // Check if LNURL resolution needed

// Lookup
node.decode(str: String) -> Result<String, ApiError> 
node.lookup_invoice(payment_hash: String) -> Result<Transaction, ApiError>
node.list_transactions(ListTransactionsParams) -> Result<Transaction, ApiError>

Node Management

node.get_info() -> Result<NodeInfo, ApiError> // returns NodeInfo and balances

Channel Management

// TODO - Not implemented
node.channel_info()

LNURL & Lightning Address

LNI supports paying to Lightning Addresses (user@domain.com) and LNURL endpoints. The lnurl module handles automatic resolution to BOLT11 invoices.

Rust

use lni::lnurl::{resolve_to_bolt11, get_payment_info, PaymentDestination};

// Auto-detect payment type
let dest = PaymentDestination::parse("nicktee@strike.me")?;
// Returns: LightningAddress { user: "nicktee", domain: "strike.me" }

// Check if destination needs LNURL resolution
let needs_resolve = lni::lnurl::needs_resolution("nicktee@strike.me"); // true
let needs_resolve = lni::lnurl::needs_resolution("lnbc10u1..."); // false

// Get payment info (fetches min/max amounts from LNURL endpoint)
let info = get_payment_info("nicktee@strike.me", Some(100_000)).await?;
println!("Min: {} msats, Max: {} msats", info.min_sendable_msats, info.max_sendable_msats);

// Resolve to BOLT11 invoice and pay
let bolt11 = resolve_to_bolt11("nicktee@strike.me", Some(100_000)).await?;
node.pay_invoice(PayInvoiceParams { invoice: bolt11, ..Default::default() }).await?;

TypeScript (Node.js)

import { detectPaymentType, needsResolution, resolveToBolt11, getPaymentInfo } from '@sunnyln/lni';

// Auto-detect payment type
const type = detectPaymentType('nicktee@strike.me'); // "lightning_address"
const type2 = detectPaymentType('lnbc10u1...'); // "bolt11"
const type3 = detectPaymentType('lno1pg...'); // "bolt12"

// Check if needs resolution
const needsResolve = needsResolution('nicktee@strike.me'); // true

// Get payment info
const info = await getPaymentInfo('nicktee@strike.me', 100_000n);
console.log(`Min: ${info.minSendableMsats}, Max: ${info.maxSendableMsats}`);

// Resolve and pay
const bolt11 = await resolveToBolt11('nicktee@strike.me', 100_000n);
await node.payInvoice({ invoice: bolt11 });

Supported Destinations | Type | Example | Resolution | |------|---------|------------| | BOLT11 | lnbc10u1p5... | None (direct pay) | | BOLT12 | lno1pg... | None (use payOffer) | | Lightning Address | user@domain.com | LNURL-pay → BOLT11 | | LNURL | lnurl1... | Decode → LNURL-pay → BOLT11 |

Event Polling

LNI does some simple event polling over http to get some basic invoice status events. Polling is used instead of a heavier grpc/pubsub (for now) to make sure the lib runs cross platform and stays lightweight.

Typescript for react-native

await node.onInvoiceEvents(
    // polling params
    {
        paymentHash: TEST_PAYMENT_HASH,
        pollingDelaySec: BigInt(3), // poll every 3 seconds
        maxPollingSec: BigInt(60), // for up to 60 seconds
    },
    // callbacks for each polling round
    // The polling ends if success or maxPollingSec timeout is hit
    {
        success(transaction: Transaction | undefined): void {
            console.log('Received success invoice event:', transaction);
            setResult('Success');
        },
        pending(transaction: Transaction | undefined): void {
            console.log('Received pending event:', transaction);
        },
        failure(transaction: Transaction | undefined): void {
            console.log('Received failure event:', transaction);
        },
    }
);

Typescript for nodejs

await node.onInvoiceEvents(
    // polling params
    {
        paymentHash: process.env.LND_TEST_PAYMENT_HASH,
        pollingDelaySec: 4,
        maxPollingSec: 60,
    }, 
    // callback for each polling round
    // The polling ends if success or maxPollingSec timeout is hit
    (status, tx) => {
        console.log("Invoice event:", status, tx);
    }
);

Rust

struct OnInvoiceEventCallback {}
impl crate::types::OnInvoiceEventCallback for OnInvoiceEventCallback {
    fn success(&self, transaction: Option<Transaction>) {
        println!("success");
    }
    fn pending(&self, transaction: Option<Transaction>) {
        println!("pending");
    }
    fn failure(&self, transaction: Option<Transaction>) {
        println!("epic fail");
    }
}
let params = crate::types::OnInvoiceEventParams {
    payment_hash: TEST_PAYMENT_HASH.to_string(),
    polling_delay_sec: 3,
    max_polling_sec: 60,
};
let callback = OnInvoiceEventCallback {};
NODE.on_invoice_events(params, Arc::new(callback));

Build

cd crates/lni
cargo clean
cargo build
cargo test

Folder Structure

lni
├── bindings
│   ├── lni_nodejs
│   ├── lni_react_native
│   ├── typescript
├── crates
│   ├── lni
│       |─── lnd
│       |─── cln
│       |─── phoenixd
│       |─── nwc
│       |─── strike
│       |─── speed
│       |─── blink

TypeScript (frontend)

npm install @sunnyln/lni

Build from source:

cd bindings/typescript
npm install
npm run typecheck
npm run build

Install TypeScript binding from GitHub repo (lightning-node-interface/lni):

TMP_DIR=$(mktemp -d) && git clone --depth 1 https://github.com/lightning-node
View on GitHub
GitHub Stars10
CategoryCustomer
Updated25d ago
Forks1

Languages

Swift

Security Score

75/100

Audited on Mar 12, 2026

No findings