@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
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Shared config generation functions for litd and lnd containers.
|
|
3
|
+
#
|
|
4
|
+
# These functions take a config template and produce a runtime config
|
|
5
|
+
# by substituting network, debug level, node alias, UI password, and
|
|
6
|
+
# appending extra arguments as config-file lines.
|
|
7
|
+
#
|
|
8
|
+
# Usage:
|
|
9
|
+
# source skills/lib/config-gen.sh
|
|
10
|
+
# generate_litd_config <template> <output> [network] [debug] [alias] [uipass] [extra_args]
|
|
11
|
+
# generate_lnd_config <template> <output> [network] [debug] [extra_args]
|
|
12
|
+
|
|
13
|
+
# generate_litd_config produces a litd runtime config from a template.
|
|
14
|
+
#
|
|
15
|
+
# It replaces the network boolean flag, debug level, node alias, and
|
|
16
|
+
# UI password in-place, then appends any extra arguments as config lines.
|
|
17
|
+
# Extra arguments are converted from CLI format (--lnd.flag=value) to
|
|
18
|
+
# config file format (lnd.flag=value). Boolean flags without a value
|
|
19
|
+
# get =true appended.
|
|
20
|
+
#
|
|
21
|
+
# Arguments:
|
|
22
|
+
# $1 - template Path to the .conf.template file.
|
|
23
|
+
# $2 - output Path to write the generated config.
|
|
24
|
+
# $3 - network Bitcoin network (default: testnet).
|
|
25
|
+
# $4 - debug Debug level string (default: info).
|
|
26
|
+
# $5 - alias Node alias (default: litd-agent).
|
|
27
|
+
# $6 - uipass litd UI password (default: empty, keeps template value).
|
|
28
|
+
# $7 - extra Space-separated extra CLI flags to append.
|
|
29
|
+
generate_litd_config() {
|
|
30
|
+
local template="$1"
|
|
31
|
+
local output="$2"
|
|
32
|
+
local network="${3:-testnet}"
|
|
33
|
+
local debug_level="${4:-info}"
|
|
34
|
+
local alias="${5:-litd-agent}"
|
|
35
|
+
local ui_password="${6:-}"
|
|
36
|
+
local extra_args="${7:-}"
|
|
37
|
+
|
|
38
|
+
cp "$template" "$output"
|
|
39
|
+
|
|
40
|
+
# Replace the litd-level network flag: network=<old> -> network=<new>.
|
|
41
|
+
# litd's global network= flag handles setting the correct lnd.bitcoin.*
|
|
42
|
+
# flag internally, avoiding the lnd default config conflict.
|
|
43
|
+
sed -i.bak -E \
|
|
44
|
+
's/^network=(testnet|mainnet|signet|regtest)/network='"$network"'/' \
|
|
45
|
+
"$output"
|
|
46
|
+
rm -f "$output.bak"
|
|
47
|
+
|
|
48
|
+
# Replace the debug level.
|
|
49
|
+
sed -i.bak 's/^lnd\.debuglevel=.*/lnd.debuglevel='"$debug_level"'/' "$output"
|
|
50
|
+
rm -f "$output.bak"
|
|
51
|
+
|
|
52
|
+
# Replace the node alias.
|
|
53
|
+
sed -i.bak 's/^lnd\.alias=.*/lnd.alias='"$alias"'/' "$output"
|
|
54
|
+
rm -f "$output.bak"
|
|
55
|
+
|
|
56
|
+
# Set UI password if provided.
|
|
57
|
+
if [ -n "$ui_password" ]; then
|
|
58
|
+
if grep -q '^uipassword=' "$output"; then
|
|
59
|
+
sed -i.bak 's/^uipassword=.*/uipassword='"$ui_password"'/' "$output"
|
|
60
|
+
rm -f "$output.bak"
|
|
61
|
+
fi
|
|
62
|
+
fi
|
|
63
|
+
|
|
64
|
+
# Append extra args as config-file lines.
|
|
65
|
+
_append_extra_args "$output" "$extra_args"
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
# generate_lnd_config produces a standalone lnd runtime config from a
|
|
69
|
+
# template. Same pattern as generate_litd_config but for lnd-style
|
|
70
|
+
# configs that use unprefixed keys (bitcoin.active, debuglevel, etc.).
|
|
71
|
+
#
|
|
72
|
+
# Arguments:
|
|
73
|
+
# $1 - template Path to the .conf.template file.
|
|
74
|
+
# $2 - output Path to write the generated config.
|
|
75
|
+
# $3 - network Bitcoin network (default: testnet).
|
|
76
|
+
# $4 - debug Debug level string (default: info).
|
|
77
|
+
# $5 - extra Space-separated extra CLI flags to append.
|
|
78
|
+
generate_lnd_config() {
|
|
79
|
+
local template="$1"
|
|
80
|
+
local output="$2"
|
|
81
|
+
local network="${3:-testnet}"
|
|
82
|
+
local debug_level="${4:-info}"
|
|
83
|
+
local extra_args="${5:-}"
|
|
84
|
+
|
|
85
|
+
cp "$template" "$output"
|
|
86
|
+
|
|
87
|
+
# Replace the network boolean: bitcoin.<old>=true -> bitcoin.<new>=true.
|
|
88
|
+
# Use -E for extended regex (portable across macOS and Linux).
|
|
89
|
+
sed -i.bak -E \
|
|
90
|
+
's/^bitcoin\.(testnet|mainnet|signet|regtest)=true/bitcoin.'"$network"'=true/' \
|
|
91
|
+
"$output"
|
|
92
|
+
rm -f "$output.bak"
|
|
93
|
+
|
|
94
|
+
# Replace the debug level.
|
|
95
|
+
sed -i.bak 's/^debuglevel=.*/debuglevel='"$debug_level"'/' "$output"
|
|
96
|
+
rm -f "$output.bak"
|
|
97
|
+
|
|
98
|
+
# Append extra args as config-file lines.
|
|
99
|
+
_append_extra_args "$output" "$extra_args"
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
# _append_extra_args converts CLI-style flags to config-file lines and
|
|
103
|
+
# appends them to the output file. Each --flag=value becomes flag=value;
|
|
104
|
+
# bare --flag becomes flag=true.
|
|
105
|
+
_append_extra_args() {
|
|
106
|
+
local output="$1"
|
|
107
|
+
local extra_args="$2"
|
|
108
|
+
|
|
109
|
+
if [ -z "$extra_args" ]; then
|
|
110
|
+
return
|
|
111
|
+
fi
|
|
112
|
+
|
|
113
|
+
echo "" >> "$output"
|
|
114
|
+
echo "# Profile/CLI extra arguments." >> "$output"
|
|
115
|
+
for arg in $extra_args; do
|
|
116
|
+
# Strip leading dashes.
|
|
117
|
+
local line="${arg#--}"
|
|
118
|
+
line="${line#-}"
|
|
119
|
+
|
|
120
|
+
# Boolean flags without =value get =true.
|
|
121
|
+
if [[ "$line" != *=* ]]; then
|
|
122
|
+
line="${line}=true"
|
|
123
|
+
fi
|
|
124
|
+
|
|
125
|
+
echo "$line" >> "$output"
|
|
126
|
+
done
|
|
127
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Shared REST call helpers for lnd/litd scripts.
|
|
3
|
+
#
|
|
4
|
+
# Provides rest_call() and wait_for_rest() functions that handle both
|
|
5
|
+
# native and container modes. In container mode, the host-mapped port
|
|
6
|
+
# is discovered via `docker port` since container images do not include
|
|
7
|
+
# curl.
|
|
8
|
+
#
|
|
9
|
+
# Required variables (set before sourcing):
|
|
10
|
+
# REST_HOST - REST API host (default: localhost).
|
|
11
|
+
# REST_PORT - REST API port (default: 8080).
|
|
12
|
+
# CONTAINER - Docker container name (empty for native mode).
|
|
13
|
+
# CONTAINER_PORT - Internal container port to map (default: $REST_PORT).
|
|
14
|
+
#
|
|
15
|
+
# Usage:
|
|
16
|
+
# source skills/lib/rest.sh
|
|
17
|
+
# rest_call GET "/v1/state"
|
|
18
|
+
# rest_call POST "/v1/initwallet" '{"wallet_password":"..."}'
|
|
19
|
+
# wait_for_rest
|
|
20
|
+
|
|
21
|
+
# rest_call issues a curl request against the lnd/litd REST API.
|
|
22
|
+
# In container mode, it discovers the host-mapped port via docker port.
|
|
23
|
+
rest_call() {
|
|
24
|
+
local method="$1"
|
|
25
|
+
local endpoint="$2"
|
|
26
|
+
local data="${3:-}"
|
|
27
|
+
|
|
28
|
+
local host="${REST_HOST:-localhost}"
|
|
29
|
+
local port="${REST_PORT:-8080}"
|
|
30
|
+
local internal_port="${CONTAINER_PORT:-$port}"
|
|
31
|
+
|
|
32
|
+
# Container mode: discover the host-mapped port for the container's
|
|
33
|
+
# internal REST port.
|
|
34
|
+
if [ -n "${CONTAINER:-}" ]; then
|
|
35
|
+
host="localhost"
|
|
36
|
+
local mapped
|
|
37
|
+
mapped=$(docker port "$CONTAINER" "$internal_port" 2>/dev/null \
|
|
38
|
+
| head -1 | sed 's/.*://')
|
|
39
|
+
if [ -n "$mapped" ]; then
|
|
40
|
+
port="$mapped"
|
|
41
|
+
fi
|
|
42
|
+
fi
|
|
43
|
+
|
|
44
|
+
if [ "$method" = "GET" ]; then
|
|
45
|
+
curl -sk -X GET \
|
|
46
|
+
"https://$host:$port$endpoint" 2>&1
|
|
47
|
+
else
|
|
48
|
+
curl -sk -X POST \
|
|
49
|
+
"https://$host:$port$endpoint" \
|
|
50
|
+
-H "Content-Type: application/json" \
|
|
51
|
+
-d "$data" 2>&1
|
|
52
|
+
fi
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
# wait_for_rest polls the REST API until it responds or times out.
|
|
56
|
+
wait_for_rest() {
|
|
57
|
+
local label="${1:-REST API}"
|
|
58
|
+
echo "Waiting for $label..."
|
|
59
|
+
for i in {1..30}; do
|
|
60
|
+
if rest_call GET "/v1/state" &>/dev/null; then
|
|
61
|
+
echo "$label is ready."
|
|
62
|
+
return 0
|
|
63
|
+
fi
|
|
64
|
+
sleep 2
|
|
65
|
+
echo " Waiting... ($i/30)"
|
|
66
|
+
done
|
|
67
|
+
echo "Error: $label did not become available." >&2
|
|
68
|
+
return 1
|
|
69
|
+
}
|
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: lightning-security-module
|
|
3
|
+
description: Set up an lnd remote signer container that holds private keys separately from the agent. Exports a credentials bundle (accounts JSON, TLS cert, admin macaroon) for watch-only litd nodes. Container-first with Docker, native fallback. Use when firewalling private key material from AI agents.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Lightning Security Module (Remote Signer)
|
|
7
|
+
|
|
8
|
+
Set up an lnd remote signer container that holds private keys on a separate,
|
|
9
|
+
secured machine. The signer never routes payments or opens channels — it only
|
|
10
|
+
holds keys and signs when asked by a watch-only litd node.
|
|
11
|
+
|
|
12
|
+
## Architecture
|
|
13
|
+
|
|
14
|
+
```
|
|
15
|
+
Agent Machine Signer Machine (secure)
|
|
16
|
+
┌─────────────────┐ ┌─────────────────────┐
|
|
17
|
+
│ litd (watch-only)│◄──gRPC───►│ lnd (signer) │
|
|
18
|
+
│ - neutrino │ │ - holds seed │
|
|
19
|
+
│ - manages chans │ │ - signs commitments │
|
|
20
|
+
│ - routes pmts │ │ - signs on-chain txs │
|
|
21
|
+
│ - NO key material│ │ - no p2p networking │
|
|
22
|
+
└─────────────────┘ └─────────────────────┘
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
The watch-only node handles all networking and channel management. The signer
|
|
26
|
+
node holds the seed and performs cryptographic signing. Even if the agent machine
|
|
27
|
+
is fully compromised, the attacker cannot extract private keys.
|
|
28
|
+
|
|
29
|
+
See [references/architecture.md](references/architecture.md) for the full
|
|
30
|
+
architecture explainer.
|
|
31
|
+
|
|
32
|
+
## Quick Start (Container — Recommended)
|
|
33
|
+
|
|
34
|
+
### On the Signer Machine
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
# 1. Install lnd signer image
|
|
38
|
+
skills/lightning-security-module/scripts/install.sh
|
|
39
|
+
|
|
40
|
+
# 2. Start signer container
|
|
41
|
+
skills/lightning-security-module/scripts/start-signer.sh
|
|
42
|
+
|
|
43
|
+
# 3. Set up signer wallet and export credentials
|
|
44
|
+
skills/lightning-security-module/scripts/setup-signer.sh
|
|
45
|
+
|
|
46
|
+
# 4. Copy the credentials bundle to the agent machine
|
|
47
|
+
# The setup script prints the bundle path and base64 string.
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### On the Agent Machine
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
# 5. Import credentials bundle
|
|
54
|
+
skills/lnd/scripts/import-credentials.sh --bundle <credentials-bundle>
|
|
55
|
+
|
|
56
|
+
# 6. Start litd in watch-only mode
|
|
57
|
+
skills/lnd/scripts/start-lnd.sh --watchonly
|
|
58
|
+
|
|
59
|
+
# 7. Create watch-only wallet
|
|
60
|
+
skills/lnd/scripts/create-wallet.sh
|
|
61
|
+
|
|
62
|
+
# 8. Check status
|
|
63
|
+
skills/lnd/scripts/lncli.sh getinfo
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### Two-Container Local Setup
|
|
67
|
+
|
|
68
|
+
For testing both on the same machine:
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
# Start litd + signer together
|
|
72
|
+
skills/lnd/scripts/start-lnd.sh --watchonly
|
|
73
|
+
|
|
74
|
+
# Set up signer wallet
|
|
75
|
+
skills/lightning-security-module/scripts/setup-signer.sh --container litd-signer
|
|
76
|
+
|
|
77
|
+
# Import credentials and create watch-only wallet
|
|
78
|
+
skills/lnd/scripts/import-credentials.sh --bundle ~/.lnget/signer/credentials-bundle
|
|
79
|
+
skills/lnd/scripts/create-wallet.sh --container litd
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## Installation
|
|
83
|
+
|
|
84
|
+
Default: pulls the lnd Docker image for the signer.
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
skills/lightning-security-module/scripts/install.sh
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
This pulls `lightninglabs/lnd:v0.20.0-beta` from Docker Hub. The signer only
|
|
91
|
+
needs plain lnd (not litd) since it only holds keys and signs.
|
|
92
|
+
|
|
93
|
+
### Build from Source (Fallback)
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
skills/lightning-security-module/scripts/install.sh --source
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
## Native Mode
|
|
100
|
+
|
|
101
|
+
For running the signer without Docker:
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
# Set up signer natively
|
|
105
|
+
skills/lightning-security-module/scripts/setup-signer.sh --native
|
|
106
|
+
|
|
107
|
+
# Start signer natively
|
|
108
|
+
skills/lightning-security-module/scripts/start-signer.sh --native
|
|
109
|
+
|
|
110
|
+
# Stop signer natively
|
|
111
|
+
skills/lightning-security-module/scripts/stop-signer.sh --native
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
## Remote Nodes
|
|
115
|
+
|
|
116
|
+
Export credentials from a remote signer:
|
|
117
|
+
|
|
118
|
+
```bash
|
|
119
|
+
skills/lightning-security-module/scripts/export-credentials.sh \
|
|
120
|
+
--rpcserver signer-host:10012 \
|
|
121
|
+
--tlscertpath ~/signer-tls.cert \
|
|
122
|
+
--macaroonpath ~/signer-admin.macaroon
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
## Credential Bundle Format
|
|
126
|
+
|
|
127
|
+
The exported bundle (`~/.lnget/signer/credentials-bundle/`) contains:
|
|
128
|
+
|
|
129
|
+
| File | Purpose |
|
|
130
|
+
|------|---------|
|
|
131
|
+
| `accounts.json` | Account xpubs for watch-only wallet import |
|
|
132
|
+
| `tls.cert` | Signer's TLS certificate for authenticated gRPC |
|
|
133
|
+
| `admin.macaroon` | Signer's admin macaroon for RPC authentication |
|
|
134
|
+
|
|
135
|
+
The bundle is also available as a single base64-encoded tar.gz file
|
|
136
|
+
(`credentials-bundle.tar.gz.b64`) for easy copy-paste transfer between machines.
|
|
137
|
+
|
|
138
|
+
## Scripts
|
|
139
|
+
|
|
140
|
+
| Script | Purpose |
|
|
141
|
+
|--------|---------|
|
|
142
|
+
| `install.sh` | Pull lnd signer image (or build from source) |
|
|
143
|
+
| `docker-start.sh` | Start signer container |
|
|
144
|
+
| `docker-stop.sh` | Stop signer container |
|
|
145
|
+
| `setup-signer.sh` | Create signer wallet and export credentials |
|
|
146
|
+
| `start-signer.sh` | Start signer (delegates to Docker by default) |
|
|
147
|
+
| `stop-signer.sh` | Stop signer (delegates to Docker by default) |
|
|
148
|
+
| `export-credentials.sh` | Re-export credentials from running signer |
|
|
149
|
+
|
|
150
|
+
## Managing the Signer
|
|
151
|
+
|
|
152
|
+
### Start
|
|
153
|
+
|
|
154
|
+
```bash
|
|
155
|
+
# Docker (default)
|
|
156
|
+
skills/lightning-security-module/scripts/start-signer.sh
|
|
157
|
+
|
|
158
|
+
# With network override
|
|
159
|
+
skills/lightning-security-module/scripts/start-signer.sh --network mainnet
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
### Stop
|
|
163
|
+
|
|
164
|
+
```bash
|
|
165
|
+
# Docker stop (preserve data)
|
|
166
|
+
skills/lightning-security-module/scripts/stop-signer.sh
|
|
167
|
+
|
|
168
|
+
# Docker stop + remove volumes
|
|
169
|
+
skills/lightning-security-module/scripts/stop-signer.sh --clean
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
### Re-export Credentials
|
|
173
|
+
|
|
174
|
+
If TLS certificates or macaroons have been regenerated:
|
|
175
|
+
|
|
176
|
+
```bash
|
|
177
|
+
skills/lightning-security-module/scripts/export-credentials.sh
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
## Configuration
|
|
181
|
+
|
|
182
|
+
### Container Config
|
|
183
|
+
|
|
184
|
+
The signer compose template is at
|
|
185
|
+
`skills/lightning-security-module/templates/docker-compose-signer.yml`. Config
|
|
186
|
+
is passed via command-line arguments.
|
|
187
|
+
|
|
188
|
+
### Native Config
|
|
189
|
+
|
|
190
|
+
The native signer config template is at
|
|
191
|
+
`skills/lightning-security-module/templates/signer-lnd.conf.template`. Key
|
|
192
|
+
differences from a standard lnd node:
|
|
193
|
+
|
|
194
|
+
- **No P2P listening** (`--listen=`) — signer doesn't route
|
|
195
|
+
- **RPC on 0.0.0.0:10012** — accepts connections from watch-only node
|
|
196
|
+
- **REST on localhost:10013** — local only, for wallet creation
|
|
197
|
+
- **TLS extra IP 0.0.0.0** — watch-only on a different machine can connect
|
|
198
|
+
- **No autopilot, no routing fees** — signer is signing-only
|
|
199
|
+
|
|
200
|
+
## Security Model
|
|
201
|
+
|
|
202
|
+
**What stays on the signer:**
|
|
203
|
+
- 24-word seed mnemonic
|
|
204
|
+
- All private keys (funding, revocation, HTLC)
|
|
205
|
+
- Wallet database with key material
|
|
206
|
+
|
|
207
|
+
**What gets exported:**
|
|
208
|
+
- Account xpubs (public keys only — cannot spend)
|
|
209
|
+
- TLS certificate (for authenticated connection)
|
|
210
|
+
- Admin macaroon (for RPC auth — scope down for production)
|
|
211
|
+
|
|
212
|
+
**Threat model:**
|
|
213
|
+
- Compromised agent machine cannot sign transactions or extract keys
|
|
214
|
+
- Attacker with agent access can see balances and channel state but not spend
|
|
215
|
+
- Signer machine should have minimal attack surface
|
|
216
|
+
|
|
217
|
+
**Production hardening:**
|
|
218
|
+
- Replace admin macaroon with a signer-only macaroon (see `macaroon-bakery`)
|
|
219
|
+
- Restrict signer RPC to specific IP addresses via firewall
|
|
220
|
+
- Run signer on dedicated hardware or a hardened VM
|
|
221
|
+
- Use Lightning Node Connect (LNC) via `mcp-lnc` for read-only agent access
|
|
222
|
+
|
|
223
|
+
## Macaroon Bakery for Signer
|
|
224
|
+
|
|
225
|
+
For production, bake a signing-only macaroon:
|
|
226
|
+
|
|
227
|
+
```bash
|
|
228
|
+
skills/macaroon-bakery/scripts/bake.sh --role signer-only \
|
|
229
|
+
--container litd-signer --rpc-port 10012
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
Then re-export the credentials bundle with the scoped macaroon.
|
|
233
|
+
|
|
234
|
+
## Container & Ports
|
|
235
|
+
|
|
236
|
+
| Container | Purpose | Ports |
|
|
237
|
+
|-----------|---------|-------|
|
|
238
|
+
| `litd-signer` | Remote signer (lnd) | 10012, 10013 |
|
|
239
|
+
|
|
240
|
+
| Port | Service | Interface | Description |
|
|
241
|
+
|-------|---------|-----------|-------------|
|
|
242
|
+
| 10012 | gRPC | 0.0.0.0 | Signer RPC (watch-only connects here) |
|
|
243
|
+
| 10013 | REST | 0.0.0.0 | REST for wallet creation |
|
|
244
|
+
|
|
245
|
+
## File Locations
|
|
246
|
+
|
|
247
|
+
| Path | Purpose |
|
|
248
|
+
|------|---------|
|
|
249
|
+
| `~/.lnget/signer/wallet-password.txt` | Signer wallet passphrase (0600) |
|
|
250
|
+
| `~/.lnget/signer/seed.txt` | Signer seed mnemonic (0600) |
|
|
251
|
+
| `~/.lnget/signer/credentials-bundle/` | Exported credentials |
|
|
252
|
+
| `~/.lnget/signer/signer-lnd.conf` | Signer config (native mode) |
|
|
253
|
+
| `versions.env` | Pinned container image versions |
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
# Remote Signer Architecture
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
The remote signer architecture splits an lnd Lightning node into two separate
|
|
6
|
+
processes running on different machines:
|
|
7
|
+
|
|
8
|
+
1. **Signer node** — holds all private key material, performs signing
|
|
9
|
+
2. **Watch-only node** — handles networking, channels, routing, payments
|
|
10
|
+
|
|
11
|
+
This separation ensures that even if the machine running the watch-only node
|
|
12
|
+
(the "agent machine") is fully compromised, the attacker cannot extract private
|
|
13
|
+
keys or sign arbitrary transactions.
|
|
14
|
+
|
|
15
|
+
## Two-Node Architecture
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
┌─────────────────────────────┐ ┌─────────────────────────────┐
|
|
19
|
+
│ Agent Machine │ │ Signer Machine │
|
|
20
|
+
│ │ │ │
|
|
21
|
+
│ ┌───────────────────────┐ │ │ ┌───────────────────────┐ │
|
|
22
|
+
│ │ lnd (watch-only) │ │ │ │ lnd (signer) │ │
|
|
23
|
+
│ │ │ │ │ │ │ │
|
|
24
|
+
│ │ - Neutrino backend │◄─┼─gRPC┼─►│ - Holds seed │ │
|
|
25
|
+
│ │ - Channel state │ │ │ │ - Signs commitments │ │
|
|
26
|
+
│ │ - Routes payments │ │ │ │ - Signs on-chain txs │ │
|
|
27
|
+
│ │ - Manages peers │ │ │ │ - No p2p networking │ │
|
|
28
|
+
│ │ - NO private keys │ │ │ │ - Minimal surface │ │
|
|
29
|
+
│ └───────────────────────┘ │ │ └───────────────────────┘ │
|
|
30
|
+
│ │ │ │
|
|
31
|
+
│ Imported from signer: │ │ Never exported: │
|
|
32
|
+
│ - accounts.json (xpubs) │ │ - Seed mnemonic │
|
|
33
|
+
│ - tls.cert │ │ - Private keys │
|
|
34
|
+
│ - admin.macaroon │ │ - Wallet DB key material │
|
|
35
|
+
└─────────────────────────────┘ └─────────────────────────────┘
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## What Gets Exported
|
|
39
|
+
|
|
40
|
+
The credential bundle contains three files:
|
|
41
|
+
|
|
42
|
+
### accounts.json
|
|
43
|
+
|
|
44
|
+
Output of `lncli wallet accounts list`. Contains extended public keys (xpubs)
|
|
45
|
+
for all lnd accounts. These allow the watch-only node to:
|
|
46
|
+
|
|
47
|
+
- Derive public keys and addresses
|
|
48
|
+
- Track on-chain balances
|
|
49
|
+
- Construct unsigned transactions
|
|
50
|
+
|
|
51
|
+
They do **not** allow signing or spending.
|
|
52
|
+
|
|
53
|
+
### tls.cert
|
|
54
|
+
|
|
55
|
+
The signer's TLS certificate. The watch-only node uses this to establish an
|
|
56
|
+
authenticated, encrypted gRPC connection to the signer. This prevents
|
|
57
|
+
man-in-the-middle attacks on the signing channel.
|
|
58
|
+
|
|
59
|
+
### admin.macaroon
|
|
60
|
+
|
|
61
|
+
The signer's admin macaroon for RPC authentication. The watch-only node presents
|
|
62
|
+
this when requesting signatures. For production, replace with a scoped macaroon
|
|
63
|
+
that only allows signing operations.
|
|
64
|
+
|
|
65
|
+
## What Never Leaves the Signer
|
|
66
|
+
|
|
67
|
+
- **Seed mnemonic** — the 24-word BIP39 phrase from which all keys derive
|
|
68
|
+
- **Private keys** — funding keys, revocation keys, HTLC keys
|
|
69
|
+
- **Wallet database key material** — encrypted private key data in the wallet DB
|
|
70
|
+
|
|
71
|
+
## Signing Flow
|
|
72
|
+
|
|
73
|
+
When the watch-only node needs a signature (e.g., committing a channel state
|
|
74
|
+
update or broadcasting an on-chain transaction):
|
|
75
|
+
|
|
76
|
+
1. Watch-only node constructs the unsigned transaction
|
|
77
|
+
2. Watch-only node sends a signing request to the signer via gRPC
|
|
78
|
+
3. Signer validates the request and produces the signature
|
|
79
|
+
4. Signer returns the signature to the watch-only node
|
|
80
|
+
5. Watch-only node assembles the signed transaction and broadcasts it
|
|
81
|
+
|
|
82
|
+
## Threat Model
|
|
83
|
+
|
|
84
|
+
### Compromised agent machine
|
|
85
|
+
|
|
86
|
+
**Impact:** Attacker can see channel states, balances, and payment history.
|
|
87
|
+
Attacker can attempt to route payments through existing channels.
|
|
88
|
+
|
|
89
|
+
**Cannot:** Extract private keys, sign arbitrary transactions, sweep funds to
|
|
90
|
+
attacker-controlled addresses, forge channel close transactions.
|
|
91
|
+
|
|
92
|
+
### Compromised signer machine
|
|
93
|
+
|
|
94
|
+
**Impact:** Full compromise — attacker has seed and all keys. Can sign arbitrary
|
|
95
|
+
transactions and sweep all funds.
|
|
96
|
+
|
|
97
|
+
**Mitigation:** The signer should have minimal attack surface. No unnecessary
|
|
98
|
+
services, no web browser, no agent software. Ideally dedicated hardware.
|
|
99
|
+
|
|
100
|
+
### Network interception (gRPC channel)
|
|
101
|
+
|
|
102
|
+
**Impact:** Without TLS cert validation, attacker could intercept signing
|
|
103
|
+
requests.
|
|
104
|
+
|
|
105
|
+
**Mitigation:** TLS with certificate pinning (the watch-only node has a copy of
|
|
106
|
+
the signer's TLS cert and validates against it).
|
|
107
|
+
|
|
108
|
+
## Macaroon Scoping
|
|
109
|
+
|
|
110
|
+
For production deployments, the admin macaroon should be replaced with a
|
|
111
|
+
custom macaroon that only grants the minimum permissions needed:
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
# On the signer, bake a signing-only macaroon
|
|
115
|
+
lncli --rpcserver=localhost:10012 --lnddir=~/.lnd-signer \
|
|
116
|
+
bakemacaroon uri:/signrpc.Signer/SignOutputRaw \
|
|
117
|
+
uri:/signrpc.Signer/ComputeInputScript \
|
|
118
|
+
uri:/signrpc.Signer/MuSig2Sign \
|
|
119
|
+
uri:/walletrpc.WalletKit/DeriveKey \
|
|
120
|
+
uri:/walletrpc.WalletKit/DeriveNextKey \
|
|
121
|
+
--save_to=~/.lnd-signer/data/chain/bitcoin/mainnet/signer-only.macaroon
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
## Future Enhancements
|
|
125
|
+
|
|
126
|
+
- **litd accounts:** Use Lightning Terminal's account system for even finer
|
|
127
|
+
access control and spending limits
|
|
128
|
+
- **Hardware signing devices:** Replace the signer lnd with a hardware security
|
|
129
|
+
module (HSM) or hardware wallet for tamper-resistant key storage
|
|
130
|
+
- **Multi-party signing:** Require multiple signers to approve high-value
|
|
131
|
+
transactions (threshold signatures)
|
|
132
|
+
- **Macaroon rotation:** Automatically rotate macaroons on a schedule to limit
|
|
133
|
+
the window of compromise
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Start the remote signer container.
|
|
3
|
+
#
|
|
4
|
+
# Usage:
|
|
5
|
+
# docker-start.sh # Default (testnet, background)
|
|
6
|
+
# docker-start.sh --network mainnet # Mainnet (real coins)
|
|
7
|
+
# docker-start.sh --foreground # Run in foreground (show logs)
|
|
8
|
+
# docker-start.sh --build # Rebuild before starting
|
|
9
|
+
|
|
10
|
+
set -e
|
|
11
|
+
|
|
12
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
13
|
+
TEMPLATE_DIR="$SCRIPT_DIR/../templates"
|
|
14
|
+
VERSIONS_FILE="$SCRIPT_DIR/../../../versions.env"
|
|
15
|
+
LIB_DIR="$SCRIPT_DIR/../../lib"
|
|
16
|
+
|
|
17
|
+
DETACH=true
|
|
18
|
+
BUILD=false
|
|
19
|
+
CUSTOM_NETWORK=""
|
|
20
|
+
|
|
21
|
+
# Source pinned versions so compose files pick them up as env vars.
|
|
22
|
+
if [ -f "$VERSIONS_FILE" ]; then
|
|
23
|
+
source "$VERSIONS_FILE"
|
|
24
|
+
export LND_VERSION LND_IMAGE
|
|
25
|
+
fi
|
|
26
|
+
|
|
27
|
+
# Source config generation functions.
|
|
28
|
+
source "$LIB_DIR/config-gen.sh"
|
|
29
|
+
|
|
30
|
+
# Parse arguments.
|
|
31
|
+
while [[ $# -gt 0 ]]; do
|
|
32
|
+
case $1 in
|
|
33
|
+
--network)
|
|
34
|
+
CUSTOM_NETWORK="$2"
|
|
35
|
+
shift 2
|
|
36
|
+
;;
|
|
37
|
+
--build)
|
|
38
|
+
BUILD=true
|
|
39
|
+
shift
|
|
40
|
+
;;
|
|
41
|
+
--foreground|-f)
|
|
42
|
+
DETACH=false
|
|
43
|
+
shift
|
|
44
|
+
;;
|
|
45
|
+
-h|--help)
|
|
46
|
+
echo "Usage: docker-start.sh [options]"
|
|
47
|
+
echo ""
|
|
48
|
+
echo "Start the remote signer container."
|
|
49
|
+
echo ""
|
|
50
|
+
echo "Options:"
|
|
51
|
+
echo " --network NET Override network (testnet, mainnet, signet)"
|
|
52
|
+
echo " --build Rebuild images before starting"
|
|
53
|
+
echo " --foreground, -f Run in foreground (show logs)"
|
|
54
|
+
exit 0
|
|
55
|
+
;;
|
|
56
|
+
*)
|
|
57
|
+
echo "Unknown option: $1" >&2
|
|
58
|
+
exit 1
|
|
59
|
+
;;
|
|
60
|
+
esac
|
|
61
|
+
done
|
|
62
|
+
|
|
63
|
+
# Override network if specified on command line.
|
|
64
|
+
if [ -n "$CUSTOM_NETWORK" ]; then
|
|
65
|
+
NETWORK="$CUSTOM_NETWORK"
|
|
66
|
+
fi
|
|
67
|
+
|
|
68
|
+
# --- Generate runtime config from template ---
|
|
69
|
+
|
|
70
|
+
LNGET_LND_DIR="${LNGET_LND_DIR:-$HOME/.lnget/lnd}"
|
|
71
|
+
mkdir -p "$LNGET_LND_DIR"
|
|
72
|
+
|
|
73
|
+
generate_lnd_config \
|
|
74
|
+
"$TEMPLATE_DIR/signer-lnd.conf.template" \
|
|
75
|
+
"$LNGET_LND_DIR/signer-lnd.conf" \
|
|
76
|
+
"${NETWORK:-testnet}" \
|
|
77
|
+
"${LND_DEBUG:-info}" \
|
|
78
|
+
""
|
|
79
|
+
|
|
80
|
+
export SIGNER_CONF_PATH="$LNGET_LND_DIR/signer-lnd.conf"
|
|
81
|
+
|
|
82
|
+
# --- Start container ---
|
|
83
|
+
|
|
84
|
+
cd "$TEMPLATE_DIR"
|
|
85
|
+
|
|
86
|
+
echo "=== Starting signer container ==="
|
|
87
|
+
echo " Config: $SIGNER_CONF_PATH"
|
|
88
|
+
echo " Network: ${NETWORK:-testnet}"
|
|
89
|
+
echo " Image: ${LND_IMAGE:-lightninglabs/lnd}:${LND_VERSION:-v0.20.0-beta}"
|
|
90
|
+
echo ""
|
|
91
|
+
|
|
92
|
+
# Build the docker-compose command.
|
|
93
|
+
CMD="docker compose -f docker-compose-signer.yml"
|
|
94
|
+
|
|
95
|
+
if [ "$BUILD" = true ]; then
|
|
96
|
+
CMD="$CMD up --build"
|
|
97
|
+
else
|
|
98
|
+
CMD="$CMD up"
|
|
99
|
+
fi
|
|
100
|
+
|
|
101
|
+
if [ "$DETACH" = true ]; then
|
|
102
|
+
CMD="$CMD -d"
|
|
103
|
+
fi
|
|
104
|
+
|
|
105
|
+
echo "Running: $CMD"
|
|
106
|
+
eval "$CMD"
|
|
107
|
+
|
|
108
|
+
if [ "$DETACH" = true ]; then
|
|
109
|
+
echo ""
|
|
110
|
+
echo "Signer started in background."
|
|
111
|
+
echo ""
|
|
112
|
+
echo "Check logs:"
|
|
113
|
+
echo " docker logs -f litd-signer"
|
|
114
|
+
echo ""
|
|
115
|
+
echo "Next: set up the signer wallet (if first run):"
|
|
116
|
+
echo " skills/lightning-security-module/scripts/setup-signer.sh --container litd-signer"
|
|
117
|
+
fi
|