소스 정보
- 저장소
- lightninglabs/lightning-agent-tools
- 최근 소스 활동
- 2026년 5월 27일 19:53
- 감지된 SKILL.md 언어
- 영어
- 스타
- 58
- 포크
- 18
설치 방법
기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.
소스 파일 검토
설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.
메뉴
기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.
설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
직접 명령은 검토 Prompt를 거치지 않습니다. 실행하기 전에 소스를 확인하세요.
npx skills add https://github.com/lightninglabs/lightning-agent-tools --skill lnc-app명령은 한 줄로 유지됩니다. 복사하기 전에 가로로 스크롤해 전체 내용을 확인하세요.
로컬 사본을 원하시나요? SkillsMP에서 현재 제공할 수 있는 파일을 다운로드하세요.
SOC 직업 분류 기준
SKILL.md 표시 중
| name | lnc-app |
| description | Guide for building a Lightning Node Connect (LNC) web application using lnc-web |
The user wants guidance on building an LNC-powered web application. Use the knowledge below to produce clear, accurate advice or code.
Lightning Node Connect lets a browser-based app communicate with an LND node without exposing any ports. The connection is end-to-end encrypted and routed through a mailbox proxy server. The user generates a pairing phrase (a BIP39-style mnemonic) in Lightning Terminal (litd) that encodes the cryptographic key material for the session. The client derives its keys from this phrase, connects to the mailbox, and performs a Noise protocol handshake with the node.
npm install @lightninglabs/lnc-web
The package ships a prebuilt UMD bundle. Import it as a default import:
import LNC from '@lightninglabs/lnc-web';
Vite config — tell Vite to pre-bundle it (converts UMD to ESM):
// vite.config.js
export default {
optimizeDeps: {
include: ['@lightninglabs/lnc-web'],
},
};
lnc-web stores everything it needs to reconnect in window.localStorage, namespaced to avoid conflicts. The credential store holds:
| Field | Description |
|---|---|
pairingPhrase | The original mnemonic — one-time use only |
serverHost | host:port of the mailbox proxy (no protocol prefix) |
localKey | Client's private key, generated on first connect |
remoteKey | Node's static public key, received on first connect |
password | Not read/written by LNC itself — exposed for your convenience |
isPaired is a read-only getter that returns true when localKey and remoteKey are both stored, meaning the session can reconnect without the pairing phrase.
The pairing phrase encodes cryptographic key material only — it does not encode the mailbox server address. The serverHost must be provided separately and is stored in the credential store after first connection.
The credential store encrypts localKey and remoteKey at rest using the password. The password is applied transparently inside the getter/setter — you never call encrypt/decrypt yourself. Set it before connecting:
lnc.credentials.password = 'user-chosen-password';
Pass pairingPhrase and password in the constructor (not as properties afterwards — the WASM module must be initialised with them):
const lnc = new LNC({
namespace: 'my-app', // isolates localStorage keys
pairingPhrase: phrase, // mnemonic from litcli
password: 'local-password',
});
// Override the mailbox if the user is not using the default
lnc.credentials.serverHost = 'mailbox.terminal.lightning.today:443';
await lnc.connect();
// IMPORTANT: clear the stored pairing phrase after success.
// On reconnect lnc-web would otherwise try to pair again with a
// one-time-use phrase and get "stream not found" from the mailbox.
lnc.credentials.pairingPhrase = '';
When isPaired is true the stored localKey/remoteKey and serverHost are all that is needed. Do not pass pairingPhrase — leave it out of the constructor entirely and clear it explicitly before connecting as a safety measure:
const lnc = new LNC({ namespace: 'my-app' });
lnc.credentials.password = 'local-password';
lnc.credentials.pairingPhrase = ''; // ensure it is never reused
await lnc.connect();
After connect() resolves, check lnc.isConnected. If it is false, read lnc.status for a human-readable reason (e.g. "Session Not Found", "Wallet Locked"). Reset the cached instance and throw so the UI can surface the message:
if (!lnc.isConnected) {
const reason = lnc.status || 'Unknown error';
lnc = null; // force a fresh instance on next attempt
throw new Error(reason);
}
Use a throwaway instance to read isPaired — do not cache the result or the instance, as you do not yet have the proxy or password:
function hasPairedCredentials() {
try {
return new LNC({ namespace: 'my-app' }).credentials.isPaired;
} catch {
return false;
}
}
lnc.disconnect();
lnc.credentials.clear(); // wipes localStorage
lnc = null;
| Setting | Default | Notes |
|---|---|---|
| Proxy server | mailbox.terminal.lightning.today:443 | Show on pairing screen only — stored in credentials, not needed again |
| Local password | — | Required; encrypts keys in localStorage |
| Pairing phrase | — | One-time use; cleared after first connect |
The proxy field should be shown only on the first-time pairing screen, not on the returning-user login screen (the stored value is used automatically). Pass the value via lnc.credentials.serverHost after constructing the instance, without a protocol prefix (wss:// is added internally by lnc-web).
All LND services are available under lnc.lnd.*. Calls are async and return plain JS objects.
const info = await lnc.lnd.lightning.getInfo();
console.log(info.alias, info.numActiveChannels);
const resp = await lnc.lnd.lightning.addInvoice({
value: '5000', // satoshis as string
memo: 'Coffee',
expiry: '300', // seconds as string
});
const { paymentRequest, rHash } = resp;
// paymentRequest is the BOLT11 string — encode it as a QR code
// rHash is the payment hash bytes — keep it to poll for settlement
Permission note:
addInvoiceis a write operation. A purereadonlyLNC session will reject it. The session must have invoice write permission.
Pass rHash back exactly as lnc-web returned it — do not attempt to convert it to hex manually:
const invoice = await lnc.lnd.lightning.lookupInvoice({ rHash: resp.rHash });
if (invoice.settled) {
// payment received
}
Poll on a timer; cancel when settled, timed out, or the user cancels:
function pollInvoice(rHash, timeoutMs, onPaid, onExpired) {
const deadline = Date.now() + timeoutMs;
let timer;
async function tick() {
if (Date.now() >= deadline) return onExpired();
const inv = await lnc.lnd.lightning.lookupInvoice({ rHash });
if (inv.settled) return onPaid(inv);
timer = setTimeout(tick, 2000);
}
timer = setTimeout(tick, 2000);
return { cancel: () => clearTimeout(timer) };
}
const resp = await lnc.lnd.lightning.listInvoices({
reversed: true, // newest first from LND
numMaxInvoices: '10',
});
const invoices = resp.invoices ?? [];
lnc.lnd.lightning.subscribeInvoices(
{},
(invoice) => {
if (invoice.settled) console.log('Paid:', invoice.paymentRequest);
},
(err) => console.error('Stream error:', err),
);
const wallet = await lnc.lnd.lightning.walletBalance();
const channel = await lnc.lnd.lightning.channelBalance();
Boot
└─ hasPairedCredentials()?
├─ No → Show pairing screen (phrase + password + proxy)
│ → pair() → clear pairingPhrase → show settings → main screen
└─ Yes → Show login screen (password only)
→ login() → main screen
On logout: disconnect() + credentials.clear() + redirect to pairing screen.