@lightninglabs/lightning-mcp-server 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +36 -0
- package/.claude-plugin/plugin.json +12 -0
- package/README.md +307 -0
- package/bin/lightning-mcp-server +15 -0
- package/docs/architecture.md +455 -0
- package/docs/commerce.md +357 -0
- package/docs/l402-and-lnget.md +267 -0
- package/docs/mcp-server.md +285 -0
- package/docs/quickref.md +263 -0
- package/docs/security.md +298 -0
- package/docs/two-agent-setup.md +394 -0
- package/package.json +52 -0
- package/postinstall.js +160 -0
- package/skills/aperture/SKILL.md +330 -0
- package/skills/aperture/scripts/install.sh +68 -0
- package/skills/aperture/scripts/setup.sh +155 -0
- package/skills/aperture/scripts/start.sh +81 -0
- package/skills/aperture/scripts/stop.sh +57 -0
- package/skills/aperture/templates/aperture-regtest.yaml +36 -0
- package/skills/aperture/templates/aperture.yaml.template +64 -0
- package/skills/aperture/templates/docker-compose-aperture.yml +59 -0
- package/skills/commerce/SKILL.md +211 -0
- package/skills/lib/config-gen.sh +127 -0
- package/skills/lib/rest.sh +69 -0
- package/skills/lightning-security-module/SKILL.md +253 -0
- package/skills/lightning-security-module/references/architecture.md +133 -0
- package/skills/lightning-security-module/scripts/docker-start.sh +117 -0
- package/skills/lightning-security-module/scripts/docker-stop.sh +53 -0
- package/skills/lightning-security-module/scripts/export-credentials.sh +268 -0
- package/skills/lightning-security-module/scripts/install.sh +178 -0
- package/skills/lightning-security-module/scripts/setup-signer.sh +307 -0
- package/skills/lightning-security-module/scripts/start-signer.sh +152 -0
- package/skills/lightning-security-module/scripts/stop-signer.sh +240 -0
- package/skills/lightning-security-module/templates/docker-compose-signer.yml +35 -0
- package/skills/lightning-security-module/templates/signer-lnd.conf.template +69 -0
- package/skills/lnd/SKILL.md +441 -0
- package/skills/lnd/profiles/debug.env +4 -0
- package/skills/lnd/profiles/default.env +3 -0
- package/skills/lnd/profiles/regtest.env +4 -0
- package/skills/lnd/profiles/taproot.env +3 -0
- package/skills/lnd/profiles/wumbo.env +3 -0
- package/skills/lnd/references/security.md +156 -0
- package/skills/lnd/scripts/create-wallet.sh +464 -0
- package/skills/lnd/scripts/docker-start.sh +256 -0
- package/skills/lnd/scripts/docker-stop.sh +109 -0
- package/skills/lnd/scripts/import-credentials.sh +145 -0
- package/skills/lnd/scripts/install.sh +195 -0
- package/skills/lnd/scripts/lncli.sh +150 -0
- package/skills/lnd/scripts/start-lnd.sh +241 -0
- package/skills/lnd/scripts/stop-lnd.sh +218 -0
- package/skills/lnd/scripts/unlock-wallet.sh +134 -0
- package/skills/lnd/templates/docker-compose-regtest.yml +122 -0
- package/skills/lnd/templates/docker-compose-watchonly.yml +71 -0
- package/skills/lnd/templates/docker-compose.yml +49 -0
- package/skills/lnd/templates/litd-regtest.conf.template +61 -0
- package/skills/lnd/templates/litd-watchonly.conf.template +57 -0
- package/skills/lnd/templates/litd.conf.template +88 -0
- package/skills/lnd/templates/lnd.conf.template +91 -0
- package/skills/lnget/SKILL.md +288 -0
- package/skills/lnget/scripts/install.sh +69 -0
- package/skills/macaroon-bakery/SKILL.md +179 -0
- package/skills/macaroon-bakery/scripts/bake.sh +337 -0
- package/skills/mcp-lnc/SKILL.md +280 -0
- package/skills/mcp-lnc/scripts/configure.sh +130 -0
- package/skills/mcp-lnc/scripts/install.sh +103 -0
- package/skills/mcp-lnc/scripts/setup-claude-config.sh +162 -0
- package/skills/mcp-lnc/templates/env.template +16 -0
- package/versions.env +23 -0
package/docs/security.md
ADDED
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
# Security Model
|
|
2
|
+
|
|
3
|
+
> How Lightning Agent Tools isolates private keys, scopes credentials, and
|
|
4
|
+
> controls what agents can do.
|
|
5
|
+
|
|
6
|
+
Agents that handle real bitcoin need a security model built for autonomous
|
|
7
|
+
operation. The core principle is straightforward: give the agent the minimum
|
|
8
|
+
credentials required for its task and keep private keys on a separate machine.
|
|
9
|
+
The kit enforces this through three tiers of access, each with different trust
|
|
10
|
+
assumptions and failure modes.
|
|
11
|
+
|
|
12
|
+
## Three Tiers of Access
|
|
13
|
+
|
|
14
|
+
```mermaid
|
|
15
|
+
graph LR
|
|
16
|
+
subgraph Tier1["Tier 1: Watch-Only + Remote Signer"]
|
|
17
|
+
direction TB
|
|
18
|
+
A1["Agent machine"]
|
|
19
|
+
S1["Signer machine"]
|
|
20
|
+
A1 --- |"gRPC/TLS"| S1
|
|
21
|
+
A1 -.- N1["No private keys"]
|
|
22
|
+
S1 -.- K1["Holds all keys"]
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
subgraph Tier2["Tier 2: Standalone"]
|
|
26
|
+
direction TB
|
|
27
|
+
A2["Agent machine"]
|
|
28
|
+
A2 -.- K2["Keys on disk<br/>(0600 permissions)"]
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
subgraph Tier3["Tier 3: Read-Only via MCP-LNC"]
|
|
32
|
+
direction TB
|
|
33
|
+
A3["Agent machine"]
|
|
34
|
+
MB["Mailbox relay"]
|
|
35
|
+
A3 --- |"encrypted WebSocket"| MB
|
|
36
|
+
A3 -.- N3["No credentials on disk<br/>Ephemeral keys only"]
|
|
37
|
+
end
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### Tier 1: Watch-Only with Remote Signer
|
|
41
|
+
|
|
42
|
+
This is the default and recommended configuration. The agent machine runs an
|
|
43
|
+
lnd node in watch-only mode. It can see balances, manage channels, and route
|
|
44
|
+
payments, but it has no private keys. All signing is delegated to a separate
|
|
45
|
+
signer node over an authenticated gRPC connection.
|
|
46
|
+
|
|
47
|
+
**What the agent machine has:**
|
|
48
|
+
- Account xpubs (public keys for address derivation)
|
|
49
|
+
- A TLS certificate and macaroon for the signer's gRPC interface
|
|
50
|
+
- Scoped macaroons for the local lnd (baked via `macaroon-bakery`)
|
|
51
|
+
|
|
52
|
+
**What the agent machine does not have:**
|
|
53
|
+
- The wallet seed
|
|
54
|
+
- Any private keys (funding, revocation, HTLC, or on-chain)
|
|
55
|
+
|
|
56
|
+
**If the agent machine is compromised,** an attacker can observe channel state,
|
|
57
|
+
balances, and payment history. They can see which peers the node is connected to
|
|
58
|
+
and the topology of its channels. But they cannot sign transactions, sweep
|
|
59
|
+
funds, or forge channel commitment updates. The keys are simply not there.
|
|
60
|
+
|
|
61
|
+
**If the signer machine is compromised,** the attacker has full control over all
|
|
62
|
+
private keys and can sign arbitrary transactions. This is a complete compromise.
|
|
63
|
+
The signer machine should have a minimal attack surface: no public-facing
|
|
64
|
+
services, restricted network access (only the watch-only node should reach port
|
|
65
|
+
10012), and ideally dedicated hardware.
|
|
66
|
+
|
|
67
|
+
**Setup:** Use the `lightning-security-module` skill on the signer machine and
|
|
68
|
+
the `lnd` skill in watch-only mode on the agent machine. See
|
|
69
|
+
[Architecture](architecture.md#remote-signer) for the signing flow.
|
|
70
|
+
|
|
71
|
+
### Tier 2: Standalone
|
|
72
|
+
|
|
73
|
+
The node generates its own seed and stores it locally. The 24-word mnemonic is
|
|
74
|
+
written to `~/.lnget/lnd/seed.txt` and the wallet passphrase to
|
|
75
|
+
`~/.lnget/lnd/wallet-password.txt`, both with mode 0600.
|
|
76
|
+
|
|
77
|
+
This mode is appropriate for:
|
|
78
|
+
- Testnet and regtest development
|
|
79
|
+
- Small-value experiments on mainnet
|
|
80
|
+
- Environments where a separate signer machine is impractical
|
|
81
|
+
|
|
82
|
+
It is **not appropriate** for production deployments with significant funds.
|
|
83
|
+
Anyone with read access to `~/.lnget/lnd/seed.txt` can reconstruct the wallet's
|
|
84
|
+
private keys.
|
|
85
|
+
|
|
86
|
+
**Setup:** Pass `--mode standalone` to `create-wallet.sh`.
|
|
87
|
+
|
|
88
|
+
### Tier 3: Read-Only via MCP-LNC
|
|
89
|
+
|
|
90
|
+
The MCP server connects to a Lightning node through an encrypted LNC tunnel
|
|
91
|
+
using a 10-word pairing phrase. No credentials are written to disk. The
|
|
92
|
+
pairing phrase is handled in memory and an ephemeral ECDSA keypair is generated
|
|
93
|
+
per session. When the session ends, the keypair is discarded.
|
|
94
|
+
|
|
95
|
+
This tier exposes 18 read-only tools. The agent can query balances, list
|
|
96
|
+
channels, decode invoices, and inspect the network graph, but it cannot send
|
|
97
|
+
payments, open channels, or modify any node state.
|
|
98
|
+
|
|
99
|
+
**If the agent machine is compromised,** the attacker gains read access to the
|
|
100
|
+
node's state for the duration of the active LNC session. Once the session is
|
|
101
|
+
closed, no credentials remain to reconnect.
|
|
102
|
+
|
|
103
|
+
**Setup:** Use the `mcp-lnc` skill. See [MCP Server](mcp-server.md) for the
|
|
104
|
+
setup walkthrough.
|
|
105
|
+
|
|
106
|
+
## Remote Signer in Depth
|
|
107
|
+
|
|
108
|
+
The remote signer splits a Lightning node into two processes. The signer runs
|
|
109
|
+
lnd with the seed and private keys but does not connect to the peer-to-peer
|
|
110
|
+
network, does not route payments, and does not manage channels. The watch-only
|
|
111
|
+
node does everything else. By default, both run in Docker containers
|
|
112
|
+
(`litd-signer` and `litd` respectively); pass `--native` to scripts for local
|
|
113
|
+
binary mode.
|
|
114
|
+
|
|
115
|
+
### Credential Bundle
|
|
116
|
+
|
|
117
|
+
When you run `setup-signer.sh`, the signer creates a wallet and exports a
|
|
118
|
+
credentials bundle to `~/.lnget/signer/credentials-bundle/`:
|
|
119
|
+
|
|
120
|
+
| File | What it contains | What it's used for |
|
|
121
|
+
|------|-----------------|-------------------|
|
|
122
|
+
| `accounts.json` | Account xpubs (public keys) | Watch-only wallet creation |
|
|
123
|
+
| `tls.cert` | Signer's TLS certificate | Authenticating the gRPC connection |
|
|
124
|
+
| `admin.macaroon` | Signer's admin macaroon | Authorizing signing RPCs |
|
|
125
|
+
|
|
126
|
+
A base64 tarball (`credentials-bundle.tar.gz.b64`) is also generated for
|
|
127
|
+
transfer. On the agent machine, `import-credentials.sh` unpacks it into
|
|
128
|
+
`~/.lnget/lnd/signer-credentials/`.
|
|
129
|
+
|
|
130
|
+
### Signing Protocol
|
|
131
|
+
|
|
132
|
+
The watch-only node constructs transactions locally and sends them to the signer
|
|
133
|
+
for signature via gRPC. The signer validates each request, signs with the
|
|
134
|
+
appropriate key, and returns the signature. The watch-only node then assembles
|
|
135
|
+
and broadcasts the signed transaction.
|
|
136
|
+
|
|
137
|
+
```mermaid
|
|
138
|
+
sequenceDiagram
|
|
139
|
+
participant WO as Watch-Only lnd<br/>(agent machine)
|
|
140
|
+
participant S as Signer lnd<br/>(secure machine)
|
|
141
|
+
participant Net as Bitcoin Network
|
|
142
|
+
|
|
143
|
+
Note over WO: Needs to close a channel
|
|
144
|
+
WO->>WO: Build unsigned commitment tx
|
|
145
|
+
WO->>S: SignOutputRaw(unsigned_tx, key_desc)
|
|
146
|
+
S->>S: Look up private key
|
|
147
|
+
S->>S: Validate request
|
|
148
|
+
S->>S: Produce signature
|
|
149
|
+
S-->>WO: Signature bytes
|
|
150
|
+
WO->>WO: Attach signature to tx
|
|
151
|
+
WO->>Net: Broadcast signed tx
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
The gRPC connection between the watch-only node and the signer is secured with
|
|
155
|
+
mutual TLS. The watch-only node uses the signer's exported TLS certificate
|
|
156
|
+
(`tls.cert`) to verify the server's identity. The macaroon provides
|
|
157
|
+
authorization.
|
|
158
|
+
|
|
159
|
+
### Hardening the Signer
|
|
160
|
+
|
|
161
|
+
For production deployments:
|
|
162
|
+
|
|
163
|
+
- **Scope the signer macaroon.** Replace `admin.macaroon` with a `signer-only`
|
|
164
|
+
macaroon baked via `macaroon-bakery`. This restricts the watch-only node to
|
|
165
|
+
signing operations and key derivation. It cannot call any other RPC on the
|
|
166
|
+
signer.
|
|
167
|
+
|
|
168
|
+
```bash
|
|
169
|
+
# Container mode (auto-detects litd-signer)
|
|
170
|
+
skills/macaroon-bakery/scripts/bake.sh --role signer-only --container litd-signer
|
|
171
|
+
|
|
172
|
+
# Native mode
|
|
173
|
+
skills/macaroon-bakery/scripts/bake.sh --role signer-only \
|
|
174
|
+
--rpc-port 10012 --lnddir ~/.lnd-signer
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
- **Firewall the signer.** Only the watch-only node's IP should be able to
|
|
178
|
+
reach port 10012. Block all other inbound traffic.
|
|
179
|
+
|
|
180
|
+
- **Dedicate the hardware.** Run the signer on a separate machine or hardened
|
|
181
|
+
VM with no other services. Minimize the installed software and disable remote
|
|
182
|
+
access except through a controlled management channel.
|
|
183
|
+
|
|
184
|
+
- **Rotate macaroons.** Bake new macaroons periodically and update the
|
|
185
|
+
watch-only node's configuration. The old root key can be revoked to
|
|
186
|
+
invalidate the previous macaroon.
|
|
187
|
+
|
|
188
|
+
## Macaroon Security
|
|
189
|
+
|
|
190
|
+
Macaroons are bearer tokens. Anyone who has a copy of a macaroon can exercise
|
|
191
|
+
its permissions against the lnd node that issued it. Treat them like passwords:
|
|
192
|
+
store them with restrictive file permissions (0600), don't commit them to
|
|
193
|
+
version control, and don't transmit them over unencrypted channels.
|
|
194
|
+
|
|
195
|
+
### Preset Roles
|
|
196
|
+
|
|
197
|
+
The `macaroon-bakery` skill provides five preset roles that cover common agent
|
|
198
|
+
use cases. Each role grants the minimum set of RPC permissions needed:
|
|
199
|
+
|
|
200
|
+
| Role | Permissions granted | Typical use case |
|
|
201
|
+
|------|-------------------|-----------------|
|
|
202
|
+
| `pay-only` | `SendPaymentSync`, `DecodePayReq`, `GetInfo` | Agent that buys L402 resources via lnget |
|
|
203
|
+
| `invoice-only` | `AddInvoice`, `LookupInvoice`, `ListInvoices`, `GetInfo` | Agent that sells resources via aperture |
|
|
204
|
+
| `read-only` | `GetInfo`, `WalletBalance`, `ChannelBalance`, `ListChannels`, `ListPeers`, `ListPayments`, `ListInvoices` | Monitoring and reporting |
|
|
205
|
+
| `channel-admin` | Everything in `read-only` + `OpenChannelSync`, `CloseChannel`, `ConnectPeer` | Node management |
|
|
206
|
+
| `signer-only` | `SignOutputRaw`, `ComputeInputScript`, `MuSig2Sign`, `DeriveKey`, `DeriveNextKey` | Remote signer credentials |
|
|
207
|
+
|
|
208
|
+
### Custom Macaroons
|
|
209
|
+
|
|
210
|
+
For permissions that don't fit a preset role, bake a custom macaroon with
|
|
211
|
+
specific URI permissions:
|
|
212
|
+
|
|
213
|
+
```bash
|
|
214
|
+
skills/macaroon-bakery/scripts/bake.sh --custom \
|
|
215
|
+
uri:/lnrpc.Lightning/SendPaymentSync \
|
|
216
|
+
uri:/lnrpc.Lightning/DecodePayReq \
|
|
217
|
+
uri:/lnrpc.Lightning/WalletBalance \
|
|
218
|
+
uri:/lnrpc.Lightning/GetInfo
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
The full list of available permission URIs is available via:
|
|
222
|
+
|
|
223
|
+
```bash
|
|
224
|
+
skills/macaroon-bakery/scripts/bake.sh --list-permissions
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
### Rotation
|
|
228
|
+
|
|
229
|
+
Macaroon rotation involves baking a new macaroon, updating the agent's
|
|
230
|
+
configuration to use it, and optionally revoking the old root key:
|
|
231
|
+
|
|
232
|
+
```bash
|
|
233
|
+
# Bake replacement
|
|
234
|
+
skills/macaroon-bakery/scripts/bake.sh --role pay-only \
|
|
235
|
+
--save-to ~/pay-only-v2.macaroon
|
|
236
|
+
|
|
237
|
+
# Update agent config to point at the new macaroon
|
|
238
|
+
|
|
239
|
+
# Revoke old root key (invalidates all macaroons baked with it)
|
|
240
|
+
skills/lnd/scripts/lncli.sh bakemacaroon --root_key_id 0
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
## Production Checklist
|
|
244
|
+
|
|
245
|
+
Before deploying the kit with real funds:
|
|
246
|
+
|
|
247
|
+
1. **Use the remote signer.** Set up `lightning-security-module` on a separate
|
|
248
|
+
machine. Run the agent's lnd in watch-only mode. Do not use standalone mode
|
|
249
|
+
for production.
|
|
250
|
+
|
|
251
|
+
2. **Scope all macaroons.** Bake role-specific macaroons for each agent. Never
|
|
252
|
+
distribute `admin.macaroon`. A buyer agent gets `pay-only`; a seller agent
|
|
253
|
+
gets `invoice-only`; a monitoring agent gets `read-only`.
|
|
254
|
+
|
|
255
|
+
3. **Scope the signer macaroon.** Replace the signer's `admin.macaroon` with
|
|
256
|
+
a `signer-only` macaroon. The watch-only node only needs signing and key
|
|
257
|
+
derivation permissions on the signer.
|
|
258
|
+
|
|
259
|
+
4. **Firewall the signer.** Restrict port 10012 to the watch-only node's IP.
|
|
260
|
+
In container mode, port 10013 (REST) is bound to `0.0.0.0` for Docker
|
|
261
|
+
networking but is only host-mapped during wallet setup. In native mode,
|
|
262
|
+
`setup-signer.sh` rebinds REST to `localhost`.
|
|
263
|
+
|
|
264
|
+
5. **Secure credential files.** Verify that `wallet-password.txt`, `seed.txt`,
|
|
265
|
+
and all `.macaroon` files have mode 0600. The kit's scripts set this
|
|
266
|
+
automatically, but verify after any manual operations.
|
|
267
|
+
|
|
268
|
+
6. **Set spending limits.** Configure `--max-cost` on lnget commands to cap
|
|
269
|
+
per-request spending. Monitor wallet balances programmatically.
|
|
270
|
+
|
|
271
|
+
7. **Back up the seed.** The signer's `seed.txt` is the only way to recover
|
|
272
|
+
funds if the signer's storage fails. Store the 24-word mnemonic securely
|
|
273
|
+
offline.
|
|
274
|
+
|
|
275
|
+
## Credential File Reference
|
|
276
|
+
|
|
277
|
+
Every credential and secret file the kit manages:
|
|
278
|
+
|
|
279
|
+
| File | Mode | Machine | Purpose |
|
|
280
|
+
|------|------|---------|---------|
|
|
281
|
+
| `~/.lnget/lnd/wallet-password.txt` | 0600 | Agent | lnd wallet unlock passphrase |
|
|
282
|
+
| `~/.lnget/lnd/seed.txt` | 0600 | Agent | 24-word mnemonic (standalone only) |
|
|
283
|
+
| `~/.lnget/lnd/signer-credentials/tls.cert` | 0644 | Agent | Signer's TLS cert |
|
|
284
|
+
| `~/.lnget/lnd/signer-credentials/admin.macaroon` | 0600 | Agent | Signer RPC auth |
|
|
285
|
+
| `~/.lnget/lnd/signer-credentials/accounts.json` | 0600 | Agent | Account xpubs |
|
|
286
|
+
| `~/.lnget/signer/wallet-password.txt` | 0600 | Signer | Signer wallet passphrase |
|
|
287
|
+
| `~/.lnget/signer/seed.txt` | 0600 | Signer | Signer 24-word mnemonic |
|
|
288
|
+
| `~/.lnd/data/chain/bitcoin/<network>/admin.macaroon` | 0600 | Agent | lnd admin macaroon |
|
|
289
|
+
| `~/.lnd-signer/data/chain/bitcoin/<network>/admin.macaroon` | 0600 | Signer | Signer admin macaroon |
|
|
290
|
+
| `~/.lnget/tokens/<domain>/` | 0700 | Agent | Cached L402 tokens |
|
|
291
|
+
| `~/.aperture/aperture.yaml` | 0600 | Seller | Aperture config (may contain credentials) |
|
|
292
|
+
|
|
293
|
+
**Container mode note:** In container deployments, daemon data directories
|
|
294
|
+
(`~/.lnd/`, `~/.lnd-signer/`) live inside Docker volumes (`litd-data`,
|
|
295
|
+
`signer-data`) rather than on the host filesystem. The host-side credential
|
|
296
|
+
files under `~/.lnget/` remain the same in both modes. Use `docker cp` or
|
|
297
|
+
`export-credentials.sh --container` to extract macaroons and TLS certs from
|
|
298
|
+
running containers.
|
|
@@ -0,0 +1,394 @@
|
|
|
1
|
+
# Two-Agent Secure Setup
|
|
2
|
+
|
|
3
|
+
> Using one agent to set up the remote signer and another to run the
|
|
4
|
+
> watch-only node.
|
|
5
|
+
|
|
6
|
+
The recommended production deployment splits key management across two
|
|
7
|
+
machines: a signer that holds private keys and a node that handles payments
|
|
8
|
+
and channels. Each machine can be operated by its own agent. The signer agent
|
|
9
|
+
runs on the secure machine and exports a credentials bundle. The node agent
|
|
10
|
+
runs on the agent-facing machine, imports that bundle, and starts a watch-only
|
|
11
|
+
lnd instance that delegates all signing back to the signer.
|
|
12
|
+
|
|
13
|
+
This walkthrough covers the full flow from both sides.
|
|
14
|
+
|
|
15
|
+
## Overview
|
|
16
|
+
|
|
17
|
+
```mermaid
|
|
18
|
+
sequenceDiagram
|
|
19
|
+
participant SA as Signer Agent<br/>(secure machine)
|
|
20
|
+
participant Transfer as Credentials Transfer
|
|
21
|
+
participant NA as Node Agent<br/>(agent machine)
|
|
22
|
+
|
|
23
|
+
SA->>SA: Install lnd
|
|
24
|
+
SA->>SA: Create signer wallet
|
|
25
|
+
SA->>SA: Start signer lnd
|
|
26
|
+
SA->>SA: Export credentials bundle
|
|
27
|
+
SA->>Transfer: credentials-bundle.tar.gz.b64
|
|
28
|
+
|
|
29
|
+
Transfer->>NA: Copy bundle to agent machine
|
|
30
|
+
|
|
31
|
+
NA->>NA: Install lnd
|
|
32
|
+
NA->>NA: Import credentials bundle
|
|
33
|
+
NA->>NA: Create watch-only wallet
|
|
34
|
+
NA->>NA: Start watch-only lnd
|
|
35
|
+
NA->>NA: Verify connection to signer
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
The credentials bundle contains three files:
|
|
39
|
+
|
|
40
|
+
| File | What it is | What it's for |
|
|
41
|
+
|------|-----------|---------------|
|
|
42
|
+
| `accounts.json` | Account xpubs (public keys only) | Creating the watch-only wallet |
|
|
43
|
+
| `tls.cert` | Signer's TLS certificate | Authenticating the gRPC connection |
|
|
44
|
+
| `admin.macaroon` | Signer's RPC token | Authorizing signing requests |
|
|
45
|
+
|
|
46
|
+
No private keys cross the wire. The bundle contains only public keys and
|
|
47
|
+
authentication material.
|
|
48
|
+
|
|
49
|
+
## Container Mode (Recommended)
|
|
50
|
+
|
|
51
|
+
Docker is the default deployment method. All scripts auto-detect containers
|
|
52
|
+
and route commands through `docker exec`.
|
|
53
|
+
|
|
54
|
+
### Signer Machine
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
# Pull the lnd signer image.
|
|
58
|
+
skills/lightning-security-module/scripts/install.sh
|
|
59
|
+
|
|
60
|
+
# Create the signer wallet and export the credentials bundle.
|
|
61
|
+
# This launches the litd-signer container, creates the wallet via REST API,
|
|
62
|
+
# and exports the bundle to ~/.lnget/signer/credentials-bundle/.
|
|
63
|
+
skills/lightning-security-module/scripts/setup-signer.sh
|
|
64
|
+
|
|
65
|
+
# Start the signer container (if not already running from setup).
|
|
66
|
+
skills/lightning-security-module/scripts/start-signer.sh
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Transfer the credentials bundle to the agent machine:
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
scp ~/.lnget/signer/credentials-bundle/credentials-bundle.tar.gz.b64 \
|
|
73
|
+
agent-machine:~/credentials-bundle.tar.gz.b64
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### Agent Machine
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
# Pull the litd image.
|
|
80
|
+
skills/lnd/scripts/install.sh
|
|
81
|
+
|
|
82
|
+
# Import the signer's credentials bundle.
|
|
83
|
+
skills/lnd/scripts/import-credentials.sh --bundle ~/credentials-bundle.tar.gz.b64
|
|
84
|
+
|
|
85
|
+
# Create the watch-only wallet (auto-detects the litd container).
|
|
86
|
+
skills/lnd/scripts/create-wallet.sh
|
|
87
|
+
|
|
88
|
+
# Start in watch-only mode (launches litd + signer containers via Docker Compose).
|
|
89
|
+
skills/lnd/scripts/start-lnd.sh --watchonly
|
|
90
|
+
|
|
91
|
+
# Verify.
|
|
92
|
+
skills/lnd/scripts/lncli.sh getinfo
|
|
93
|
+
skills/lnd/scripts/lncli.sh newaddress p2tr
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
The `--watchonly` flag uses `docker-compose-watchonly.yml`, which starts both a
|
|
97
|
+
litd container (watch-only) and connects it to the signer via the Docker
|
|
98
|
+
network. The signer's gRPC address is pre-configured in the watch-only template
|
|
99
|
+
(`lnd.remotesigner.rpchost=signer:10012`).
|
|
100
|
+
|
|
101
|
+
### Container Lifecycle
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
# Stop containers.
|
|
105
|
+
skills/lnd/scripts/stop-lnd.sh
|
|
106
|
+
|
|
107
|
+
# Stop and remove volumes (destructive — removes chain data).
|
|
108
|
+
skills/lnd/scripts/stop-lnd.sh --clean
|
|
109
|
+
|
|
110
|
+
# On signer machine.
|
|
111
|
+
skills/lightning-security-module/scripts/stop-signer.sh
|
|
112
|
+
skills/lightning-security-module/scripts/stop-signer.sh --clean
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### Scoping the Signer Macaroon (Container)
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
# On the signer machine, bake a scoped macaroon inside the container.
|
|
119
|
+
skills/macaroon-bakery/scripts/bake.sh --role signer-only --container litd-signer
|
|
120
|
+
|
|
121
|
+
# Re-export the credentials bundle with the scoped macaroon.
|
|
122
|
+
skills/lightning-security-module/scripts/export-credentials.sh --container litd-signer
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
---
|
|
126
|
+
|
|
127
|
+
## Native Mode
|
|
128
|
+
|
|
129
|
+
For environments without Docker, all scripts accept a `--native` flag to use
|
|
130
|
+
locally installed binaries.
|
|
131
|
+
|
|
132
|
+
### Part 1: Signer Agent (Secure Machine)
|
|
133
|
+
|
|
134
|
+
The signer agent runs on a machine with restricted access. This machine will
|
|
135
|
+
hold all private keys and never connect to the Lightning Network directly.
|
|
136
|
+
|
|
137
|
+
### Install lnd
|
|
138
|
+
|
|
139
|
+
```bash
|
|
140
|
+
skills/lightning-security-module/scripts/install.sh --source
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
This builds lnd from source with the signing-related build tags. The binary is
|
|
144
|
+
the same as a regular lnd install, but the signer's configuration restricts it
|
|
145
|
+
to signing operations only.
|
|
146
|
+
|
|
147
|
+
### Create the signer wallet and export credentials
|
|
148
|
+
|
|
149
|
+
```bash
|
|
150
|
+
skills/lightning-security-module/scripts/setup-signer.sh --native
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
This does three things:
|
|
154
|
+
|
|
155
|
+
1. Generates a new wallet with a random passphrase and 24-word seed mnemonic.
|
|
156
|
+
Both are saved to `~/.lnget/signer/` with mode 0600.
|
|
157
|
+
2. Starts the signer lnd temporarily to extract account xpubs.
|
|
158
|
+
3. Exports the credentials bundle to
|
|
159
|
+
`~/.lnget/signer/credentials-bundle/`.
|
|
160
|
+
|
|
161
|
+
The bundle directory contains the three files listed above plus a
|
|
162
|
+
base64-encoded tarball (`credentials-bundle.tar.gz.b64`) for easy transfer.
|
|
163
|
+
|
|
164
|
+
### Start the signer
|
|
165
|
+
|
|
166
|
+
```bash
|
|
167
|
+
skills/lightning-security-module/scripts/start-signer.sh --native
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
The signer listens on port 10012 (gRPC) for signing requests from the
|
|
171
|
+
watch-only node. In native mode, REST binds to `localhost:10013` only. It does
|
|
172
|
+
not connect to any peers and does not participate in the Lightning Network.
|
|
173
|
+
|
|
174
|
+
### Transfer the bundle
|
|
175
|
+
|
|
176
|
+
The base64-encoded bundle needs to reach the agent machine. How you transfer
|
|
177
|
+
it depends on your environment:
|
|
178
|
+
|
|
179
|
+
```bash
|
|
180
|
+
# Option 1: scp
|
|
181
|
+
scp ~/.lnget/signer/credentials-bundle/credentials-bundle.tar.gz.b64 \
|
|
182
|
+
agent-machine:~/credentials-bundle.tar.gz.b64
|
|
183
|
+
|
|
184
|
+
# Option 2: Print to terminal and copy-paste
|
|
185
|
+
cat ~/.lnget/signer/credentials-bundle/credentials-bundle.tar.gz.b64
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
The bundle contains a macaroon (bearer token) and a TLS certificate. Treat it
|
|
189
|
+
like a password during transfer.
|
|
190
|
+
|
|
191
|
+
### Optional: scope the signer macaroon
|
|
192
|
+
|
|
193
|
+
The exported `admin.macaroon` grants full RPC access to the signer. For
|
|
194
|
+
production, replace it with a `signer-only` macaroon that restricts the
|
|
195
|
+
watch-only node to signing and key derivation:
|
|
196
|
+
|
|
197
|
+
```bash
|
|
198
|
+
skills/macaroon-bakery/scripts/bake.sh --role signer-only \
|
|
199
|
+
--rpc-port 10012 --lnddir ~/.lnd-signer
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
Then re-export the credentials bundle with the scoped macaroon:
|
|
203
|
+
|
|
204
|
+
```bash
|
|
205
|
+
skills/lightning-security-module/scripts/export-credentials.sh
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
### Part 2: Node Agent (Agent Machine)
|
|
209
|
+
|
|
210
|
+
The node agent runs on the machine where agents will operate. This machine
|
|
211
|
+
will have no private keys.
|
|
212
|
+
|
|
213
|
+
### Install lnd
|
|
214
|
+
|
|
215
|
+
```bash
|
|
216
|
+
skills/lnd/scripts/install.sh --source
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
### Import the credentials bundle
|
|
220
|
+
|
|
221
|
+
```bash
|
|
222
|
+
skills/lnd/scripts/import-credentials.sh \
|
|
223
|
+
--bundle ~/credentials-bundle.tar.gz.b64
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
This unpacks the bundle into `~/.lnget/lnd/signer-credentials/`, placing
|
|
227
|
+
`accounts.json`, `tls.cert`, and `admin.macaroon` where the watch-only node
|
|
228
|
+
expects them.
|
|
229
|
+
|
|
230
|
+
### Create the watch-only wallet
|
|
231
|
+
|
|
232
|
+
```bash
|
|
233
|
+
skills/lnd/scripts/create-wallet.sh --native \
|
|
234
|
+
--signer-host <signer-ip>:10012
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
Replace `<signer-ip>` with the signer machine's IP address or hostname. This
|
|
238
|
+
creates a wallet that imports the account xpubs from the credentials bundle.
|
|
239
|
+
The wallet has no seed and no private keys. It generates a random passphrase
|
|
240
|
+
stored at `~/.lnget/lnd/wallet-password.txt` (mode 0600).
|
|
241
|
+
|
|
242
|
+
The `--signer-host` flag tells the wallet creation process where to find the
|
|
243
|
+
signer for initial key verification.
|
|
244
|
+
|
|
245
|
+
### Start the watch-only node
|
|
246
|
+
|
|
247
|
+
```bash
|
|
248
|
+
skills/lnd/scripts/start-lnd.sh --native \
|
|
249
|
+
--signer-host <signer-ip>:10012
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
The node starts with `remotesigner.enable=true` in its config, pointing at
|
|
253
|
+
the signer's gRPC address. It connects to the Bitcoin network via Neutrino,
|
|
254
|
+
syncs headers, and begins normal operation. Any transaction that requires a
|
|
255
|
+
signature (channel opens, closes, on-chain sends) is forwarded to the signer
|
|
256
|
+
over the authenticated gRPC connection.
|
|
257
|
+
|
|
258
|
+
### Verify
|
|
259
|
+
|
|
260
|
+
```bash
|
|
261
|
+
skills/lnd/scripts/lncli.sh getinfo
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
The node should report synced status. To confirm the signer connection is
|
|
265
|
+
working, try generating a new address (which requires key derivation from the
|
|
266
|
+
signer):
|
|
267
|
+
|
|
268
|
+
```bash
|
|
269
|
+
skills/lnd/scripts/lncli.sh newaddress p2tr
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
If this returns an address, the watch-only node and signer are communicating
|
|
273
|
+
correctly.
|
|
274
|
+
|
|
275
|
+
## What Each Agent Can See
|
|
276
|
+
|
|
277
|
+
After setup, the two agents have different views of the system:
|
|
278
|
+
|
|
279
|
+
```mermaid
|
|
280
|
+
graph LR
|
|
281
|
+
subgraph Signer["Signer Agent"]
|
|
282
|
+
S_seed["Wallet seed (24 words)"]
|
|
283
|
+
S_keys["All private keys"]
|
|
284
|
+
S_pass["Wallet passphrase"]
|
|
285
|
+
S_mac["Signer macaroons"]
|
|
286
|
+
end
|
|
287
|
+
|
|
288
|
+
subgraph Node["Node Agent"]
|
|
289
|
+
N_xpubs["Account xpubs (public)"]
|
|
290
|
+
N_cert["Signer TLS cert"]
|
|
291
|
+
N_smac["Signer macaroon (scoped)"]
|
|
292
|
+
N_pass["Node wallet passphrase"]
|
|
293
|
+
N_mac["Node macaroons"]
|
|
294
|
+
N_state["Channel state, balances, peers"]
|
|
295
|
+
end
|
|
296
|
+
|
|
297
|
+
subgraph Neither["Neither Agent Has"]
|
|
298
|
+
X1["Cross-machine shell access"]
|
|
299
|
+
X2["Each other's wallet passphrase"]
|
|
300
|
+
end
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
The node agent can see balances, channel state, and payment history. It can
|
|
304
|
+
initiate payments and open channels (the signing is handled transparently by
|
|
305
|
+
the signer). It cannot extract private keys because they are not on its
|
|
306
|
+
machine.
|
|
307
|
+
|
|
308
|
+
The signer agent can see its own wallet state but has no visibility into the
|
|
309
|
+
watch-only node's channels or payment activity. It responds to signing
|
|
310
|
+
requests but does not initiate any network activity.
|
|
311
|
+
|
|
312
|
+
## Network Requirements
|
|
313
|
+
|
|
314
|
+
The watch-only node needs to reach the signer on port 10012 (gRPC over TLS).
|
|
315
|
+
This is the only network path between the two machines.
|
|
316
|
+
|
|
317
|
+
```mermaid
|
|
318
|
+
graph LR
|
|
319
|
+
WO["Watch-Only Node"] -->|"TCP :10012<br/>gRPC/TLS"| S["Signer"]
|
|
320
|
+
WO -->|"TCP :9735<br/>Lightning P2P"| LN["Lightning Network"]
|
|
321
|
+
WO -->|"TCP :18333<br/>Neutrino"| BTC["Bitcoin Peers"]
|
|
322
|
+
|
|
323
|
+
S -.-x|"No outbound<br/>connections"| LN
|
|
324
|
+
S -.-x|"No outbound<br/>connections"| BTC
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
The signer should be firewalled to accept connections only from the watch-only
|
|
328
|
+
node's IP on port 10012. It makes no outbound connections.
|
|
329
|
+
|
|
330
|
+
## Ongoing Operations
|
|
331
|
+
|
|
332
|
+
Once both sides are running, day-to-day operations happen on the node agent's
|
|
333
|
+
machine:
|
|
334
|
+
|
|
335
|
+
```bash
|
|
336
|
+
# Check node status
|
|
337
|
+
skills/lnd/scripts/lncli.sh getinfo
|
|
338
|
+
|
|
339
|
+
# Fund the wallet
|
|
340
|
+
skills/lnd/scripts/lncli.sh newaddress p2tr
|
|
341
|
+
skills/lnd/scripts/lncli.sh walletbalance
|
|
342
|
+
|
|
343
|
+
# Open channels
|
|
344
|
+
skills/lnd/scripts/lncli.sh connect <pubkey>@<host>:9735
|
|
345
|
+
skills/lnd/scripts/lncli.sh openchannel --node_key=<pubkey> --local_amt=1000000
|
|
346
|
+
|
|
347
|
+
# Send payments
|
|
348
|
+
skills/lnd/scripts/lncli.sh sendpayment --pay_req=<bolt11>
|
|
349
|
+
|
|
350
|
+
# Use lnget for L402
|
|
351
|
+
lnget --max-cost 500 https://api.example.com/data
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
The signer agent's role is maintenance: keeping the signer process running,
|
|
355
|
+
rotating macaroons periodically, and backing up the seed.
|
|
356
|
+
|
|
357
|
+
```bash
|
|
358
|
+
# On the signer machine (container mode)
|
|
359
|
+
skills/lightning-security-module/scripts/start-signer.sh # delegates to docker-start.sh
|
|
360
|
+
skills/lightning-security-module/scripts/stop-signer.sh # delegates to docker-stop.sh
|
|
361
|
+
|
|
362
|
+
# Rotate the signer macaroon (container)
|
|
363
|
+
skills/macaroon-bakery/scripts/bake.sh --role signer-only --container litd-signer
|
|
364
|
+
skills/lightning-security-module/scripts/export-credentials.sh --container litd-signer
|
|
365
|
+
|
|
366
|
+
# Rotate the signer macaroon (native)
|
|
367
|
+
skills/macaroon-bakery/scripts/bake.sh --role signer-only \
|
|
368
|
+
--rpc-port 10012 --lnddir ~/.lnd-signer
|
|
369
|
+
skills/lightning-security-module/scripts/export-credentials.sh --native
|
|
370
|
+
|
|
371
|
+
# Then transfer the new bundle to the node agent and re-import
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
## Credential Scoping for the Node Agent
|
|
375
|
+
|
|
376
|
+
After the watch-only node is running, bake scoped macaroons for the node
|
|
377
|
+
agent's specific tasks. Do not leave the node agent using `admin.macaroon`:
|
|
378
|
+
|
|
379
|
+
```bash
|
|
380
|
+
# On the node agent's machine
|
|
381
|
+
|
|
382
|
+
# For a buyer agent (pays for L402 resources)
|
|
383
|
+
skills/macaroon-bakery/scripts/bake.sh --role pay-only
|
|
384
|
+
|
|
385
|
+
# For a seller agent (generates invoices via aperture)
|
|
386
|
+
skills/macaroon-bakery/scripts/bake.sh --role invoice-only
|
|
387
|
+
|
|
388
|
+
# For a monitoring agent
|
|
389
|
+
skills/macaroon-bakery/scripts/bake.sh --role read-only
|
|
390
|
+
```
|
|
391
|
+
|
|
392
|
+
This gives you two layers of credential scoping: the signer macaroon limits
|
|
393
|
+
what the watch-only node can ask the signer to do, and the node macaroon
|
|
394
|
+
limits what the agent can ask the watch-only node to do.
|