📘 Setup guide

Run LN Operator on your node

Built for an LND node on a unix machine, but works anywhere LND's REST API is reachable. About 15 minutes end to end.

What you need

1 · Install

# clone and set up a virtualenv
git clone https://github.com/lnbright/ln-operator.git
cd ln-operator
python3 -m venv venv
venv/bin/pip install -r requirements.txt

# make the CLI callable from anywhere
sudo ln -s "$PWD/ln-operator" /usr/local/bin/ln-operator

2 · Check the install

Run ln-operator --help. If you see the full subcommand list, the wrapper and virtualenv are wired up correctly:

ln-operator --help

usage: ln-operator [-h] [--no-telegram] command ...

LN Operator — Lightning Node Channel Management

positional arguments:
  command             See 'ln-operator <command> --help' for details
    pipeline          [automated] Run full pipeline: adjust_fees →
                      rebalance_channels → sync_routing → healthcheck
    plan              [feature] Channel investment plan — reads wallet
                      balance, proposes allocation
    status            [feature] Quick node overview with channel balance bars
    history           [feature] Show fee revenue, rebalance stats, and alerts
                      from database
    adjust_fees       [debug] Adjust channel fee rates based on current
                      balance ratios
    rebalance_channels
                      [debug] Move sats from overfull to depleted channels
                      (auto-chunks: halves down to 100k on route failure)
    manual_rebalance  [feature] Rebalance a SPECIFIC source→target pair
                      (recorded as manual)
    sync_routing      [debug] Sync routing events from LND into the local
                      database
    healthcheck       [debug] Snapshot channel states, check for problems,
                      fire alerts
    backup            [automated] Push channel.backup to remote host (called
                      by systemd)
    overwrite_fee     [feature] Pin a channel's outbound fee — pipeline will
                      leave it alone
    clear_fee         [feature] Remove a fee pin so the pipeline can auto-
                      adjust again
    recompute_signals
                      [automated] Refresh slow per-channel signals (market
                      multiplier). Designed for a nightly cron.
    refresh_graph     [automated] Pull the network graph into the local cache
                      (multi-MB; daily cron ahead of daily-check).
    suggest_peers     [feature] Suggest peers to open toward a target (alias
                      or pubkey) for cheaper refills.
    monitor_htlcs     [automated] Long-running: record dropped forwards from
                      LND's HTLC event stream (systemd)

options:
  -h, --help          show this help message and exit
  --no-telegram       Skip Telegram notifications

3 · Configure

Copy the template to .env and fill it in with your own node's details — this is the only file you must edit:

cp .env.example .env
nano .env

What each key means:

# ── LND connection (required) ──────────────────────────
LND_REST_URL=https://127.0.0.1:9000          # your LND REST endpoint
LND_CERT=/home/lnd/tls.cert                  # path to LND's TLS cert
LND_MACAROON=/.../admin.macaroon             # admin macaroon (read/write)

# ── Telegram alerts (optional) ─────────────────────────
TELEGRAM_BOT_TOKEN=                          # from @BotFather
TELEGRAM_CHAT_ID=                            # your chat id (via @userinfobot)

# ── Off-site channel.backup upload (optional, recommended) ──
BACKUP_SOURCE_PATH=/home/lnd/.../channel.backup  # file to push
BACKUP_SSH_HOST=IPADDRESS                        # destination host (blank = disabled)
BACKUP_SSH_USER=joe                              # destination usarname
BACKUP_SSH_PORT=22                               # destination host's ssh port
BACKUP_DEST_DIR=/home/joe/lnd_backup             # remote directory

# ── Dashboard bind (optional) ──────────────────────────
DASHBOARD_BIND_IP=127.0.0.1                  # or e.g. set your Tailscale IP for tailnet access
DASHBOARD_PORT=4000

Anything you leave blank falls back to the defaults in config.py. Tuning knobs (fee margin, rebalance budgets) live there too.

Then initialise the database and confirm the connection:

venv/bin/python3 db.py
ln-operator status        # should print your node + channel overview

4 · Automate

Three cron lines do the automation — the fast pipeline every 2 hours, a nightly refresh of the slow per-channel signals (the market multiplier that drives demand-based pricing), and a nightly pull of the network graph into a local cache (used by plan, suggest_peers, and the daily-check agent's capital suggestions):

# crontab -e
# fast loop — fees → rebalance → sync → healthcheck
0 */2 * * * cd /path/to/ln-operator && ./ln-operator pipeline 2>&1

# nightly — refresh slow market signals (per-channel market multiplier)
15 3 * * * cd /path/to/ln-operator && ./ln-operator recompute_signals >> logs/signals.log 2>&1

# nightly — cache the network graph (multi-MB pull; runs ahead of the daily check)
30 3 * * * cd /path/to/ln-operator && ./ln-operator refresh_graph >> logs/graph.log 2>&1

👉 The 2-hour pipeline is the only loop you need for fees and rebalancing — both the outbound fee engine and the optional node-level inbound-fee ladder run inside it. To turn the inbound-fee layer on, set INBOUND_FEE_ENABLED = True in config.py (it defends a depleted channel with a small negative inbound fee to pull an organic refill before paying for a circular rebalance); no extra cron line — the next pipeline run picks it up.

5 · Forwarding-failure monitor (recommended)

One piece does not run on cron: the HTLC listener (monitor_htlcs). It subscribes to LND's live HTLC event stream and records dropped forwards — the ones you couldn't route for lack of liquidity — into the database. LND persists these nowhere: the stream is live-only with no replay, so it has to run continuously as an always-on service, not every 2 hours. It feeds the dashboard's lost-revenue watch and the fast-drain fee bump, so install it like the dashboard unit:

# 1. copy the unit into systemd
sudo cp services/lnd-htlc-monitor.service /etc/systemd/system/

# 2. edit User=, WorkingDirectory=, ExecStart= and EnvironmentFile=
#    to match your install (the shipped file assumes /home/pi/ln-operator)
sudo nano /etc/systemd/system/lnd-htlc-monitor.service

# 3. reload systemd, enable at boot, and start it now
sudo systemctl daemon-reload
sudo systemctl enable --now lnd-htlc-monitor

# 4. confirm it's up (Restart=always keeps it alive across LND restarts)
systemctl status lnd-htlc-monitor
journalctl -u lnd-htlc-monitor -f

⚠️ While this service is down you are blind to dropped forwards, not clean — any downtime is a gap in the data, so let systemd keep it running.

6 · The daily check

A thin cron wrapper, scripts/daily-check.sh, runs once a day and always sends a summary to Telegram. It has two modes. By default it runs only deterministic, read-only checks — the reconcile data-integrity arithmetic plus the unit suite (make test) — and Telegrams a pass/fail summary with any issues inline. No LLM, no spend.

Flip one env var and the same daily run becomes a Claude Code skill instead: the accumulated operating judgment — what to check, what's noise vs. a real problem — as a reusable agent. There is nothing to download: it's already in the repo you cloned from GitHub. The agentic prompt lives at scripts/daily-check-prompt.md; the wrapper runs Claude headless against it (claude -p), caps spend at --max-budget-usd 5, then sends the richer summary to Telegram.

The agent is off by default and gated behind one env var. Add this line to your crontab (crontab -e) — it fires at 09:00 daily and forces a read-only macaroon so the unattended agent can never move funds. Set the flag to 1 to run the agent, or 0 for the deterministic checks:

# daily check at 09:00 — flag=1 runs the AI agent, 0 runs deterministic checks
0 9 * * * LN_OPERATOR_ENABLE_AI_DAILY_CHECK=0 \
  DAILY_CHECK_LND_MACAROON=/home/pi/.lnd-macaroons/ln-operator-readonly.macaroon \
  /home/pi/ln-operator/scripts/daily-check.sh

With LN_OPERATOR_ENABLE_AI_DAILY_CHECK=1 the AI agent runs; with 0 (or the flag absent) the deterministic checks run instead — either way the daily run happens and Telegrams you. Remove the whole line to stop the daily check entirely.

7 · Dashboard (optional)

For a quick look, just run it:

venv/bin/python3 dashboard/app.py

To keep it running across reboots, install the bundled systemd unit. It ships in services/lnd-dashboard.service — copy it into place, adjust the paths and user to match your install, then enable it:

# 1. copy the unit into systemd
sudo cp services/lnd-dashboard.service /etc/systemd/system/

# 2. edit User=, WorkingDirectory= and ExecStart= to match your install
#    path and virtualenv (the shipped file assumes /home/pi/ln-operator)
sudo nano /etc/systemd/system/lnd-dashboard.service

# 3. reload systemd, enable at boot, and start it now
sudo systemctl daemon-reload
sudo systemctl enable --now lnd-dashboard

# 4. confirm it's up, and follow the logs
systemctl status lnd-dashboard
journalctl -u lnd-dashboard -f

👉 It binds to 127.0.0.1 by default; set DASHBOARD_BIND_IP in .env to your preferred private network IP address — e.g. your Tailscale IP for tailnet access.

⚠️ We suggest serving it on a private network only — Tailscale or LAN. Never port-forward it to the open internet, as it has no authentication.

👉 Want to know what every card on the dashboard shows? See the dashboard, card by card in the tech docs.