Hardware Wallet Integration for Self-Hosted Lightning
The integration pattern: hardware wallet holds the seed, node holds the channel keys derived from the seed, signing for high-value operations crosses from the node to the hardware wallet and back. Straightforward in principle, finicky in detail. This is the working setup on the sovgrid Lightning stack with a BitBox02 in front of an LND-based node, plus the three integration mistakes I made before it worked.
Quick Take
- Architecture: BitBox02 holds the master seed. The LND node has xpub-derived channel keys that handle day-to-day channel operations. High-value operations (channel close, large on-chain spend, recovery) require the hardware wallet to sign.
- The key separation: the seed is on the hardware wallet exclusively. The node has derivation paths but never sees the raw seed. A compromised node loses channel funds but not the recovery authority.
- The integration tools: BitBoxApp for the wallet side, LND with the appropriate
walletunlockerconfiguration, optionally Specter Desktop or Sparrow for multisig variants.- The three mistakes I made: mixing up derivation paths, exposing the seed during setup, and not testing the recovery procedure before relying on it.
Why a hardware wallet at all
The threat model for a self-hosted Lightning node: the node host is a Linux machine on the operator’s network. The machine has CVEs, open ports, software dependencies, and at some point will be exposed to malicious traffic. The probability that the node host is compromised over a multi-year operational window is not zero.
If the node host holds the seed, a host compromise is a total loss. The attacker exfiltrates the seed, derives every key from it, and drains all funds (channels and on-chain).
If the node host holds only the channel keys derived from the seed, a host compromise is bounded. The attacker can operate the channels (drain channel funds, force-close), but cannot recover the seed and cannot derive any other keys. The recovery authority remains on the hardware wallet.
The reduction in blast radius is the reason hardware wallets exist for Lightning. The cost is the operational complexity of the integration; the benefit is the bounded-loss property.
The BitBox02 specifically
The BitBox02 (BitBox Swiss) is the hardware wallet sovgrid uses. As of May 2026, I am on firmware version 9.22.0 with BitBoxApp version 4.46.0. The reasons this combination works for the Lightning use case:
- Open-source firmware (the firmware source is auditable, which means reproducible builds are possible)
- USB-C interface that works without proprietary drivers on Linux, specifically without the udev rules gymnastics that Ledger requires
- Native LND integration through the BitBoxApp’s PSBT import/sign/export flow
- Swiss jurisdiction: the company (Shift Crypto AG) is based in Zurich, which is relevant for support and warranty
- Reasonable price point compared to Coldcard, which runs approximately 50% more
Coldcard is the alternative I evaluated most seriously. The PSBT tooling is mature and the Bitcoin-only focus is a genuine advantage. I use BitBox02 rather than Coldcard because the BitBoxApp’s LND integration is more straightforward for the specific watch-only xpub workflow I described above. For an air-gapped setup, Coldcard is probably the better choice.
For the broader hardware-wallet selection reasoning, see Setup: BitBox Hardware Wallet which has the day-one setup notes. The BitBox02 is available directly from BitBox Swiss (affiliate link, which means I get a small referral if you buy through it).
The setup flow
The setup happens once and shapes the multi-year operational posture. I ran this in May 2026 on LND 0.18.4-beta with BitBox02 firmware version 9.22.0. The steps below are what I actually executed, not what the docs say should work.
Phase 1: generate the seed. Power up the BitBox02, follow the on-device prompts to generate a new 24-word seed phrase. The seed appears on the device’s display; it never appears on a network-connected screen.
Write the seed phrase on a physical medium (steel plate is the durable option; paper is cheap and works if stored carefully). Store the medium in a secure location separate from the node hardware.
Phase 2: extract the xpub for the node. Use the BitBoxApp to extract the extended public key (xpub) for the derivation path the node will use. For Lightning channel funds, this is typically m/84'/0'/0' (native SegWit), which means LND uses that BIP-84 derivation path specifically for all on-chain funding outputs. The xpub is enough information to derive the public keys (the address chain) without exposing the private keys.
LND’s lncli exposes the derivation path it expects at startup. Check it before importing the xpub, because the path LND generates internally (specifically m/1017'/0'/0'/0/0 for its internal wallet) differs from the standard BIP-84 path BitBoxApp uses by default:
lncli --rpcserver=localhost:10009 walletbalance
# also reveals the address format in use: np2wkh vs p2wkh
If the output shows np2wkh (P2SH-wrapped SegWit), the node was initialized with the older path and you need to configure BitBoxApp to m/49'/0'/0' instead.
Phase 3: configure LND with the xpub. LND is set up in “watch-only” mode for the funding wallet, with the xpub as the wallet’s source of truth. I used the lnd --noseedbackup flag combined with a pre-generated wallet so the node wallet key material comes from the hardware wallet derivation, not a locally-stored seed:
lncli --rpcserver=localhost:10009 createwatchonlywallet \
--master_key_birthday=1700000000 \
--master_key_family=0 \
--accounts='[{"purpose":84,"coin_type":0,"account":0,"xpub":"<XPUB_FROM_BITBOXAPP>"}]'
The --master_key_birthday is the Unix timestamp for the day you generated the seed, which means the initial block scan starts from that date rather than from genesis. For a seed generated in May 2026, that is approximately 1748736000. The scan takes about 20 minutes on a pruned node.
Phase 4: create and verify the channel backup. Before opening any channels, write the Static Channel Backup (SCB) path to a location outside the node directory, specifically to a redundant volume. LND stores its SCB at /home/lnd/.lnd/data/chain/bitcoin/mainnet/channel.backup:
# copy the backup to a separate mount point immediately after channel opens
cp /home/lnd/.lnd/data/chain/bitcoin/mainnet/channel.backup \
/mnt/backup/lnd/channel.backup.$(date +%Y%m%d-%H%M%S)
# verify the backup is readable (non-empty, parseable)
lncli --rpcserver=localhost:10009 verifychanbackup \
--single_chan_backup "$(cat /home/lnd/.lnd/data/chain/bitcoin/mainnet/channel.backup | base64)"
Run this copy after every channel open or close. I set up a cron job to run it every 6 hours, which means the worst-case loss window is a 6-hour-old backup rather than a complete loss.
Phase 5: signing flow for on-chain spends. When the node needs to sign an on-chain transaction (channel open, channel close, sweep), LND constructs a partially-signed Bitcoin transaction (PSBT) and the operator transfers the PSBT to the BitBoxApp, signs on the hardware wallet, and transfers the signed PSBT back to the node. The flow is two minutes of work for each on-chain operation.
The PSBT export looks like this in practice:
# initiate a cooperative channel close, output the PSBT for hardware signing
lncli --rpcserver=localhost:10009 closechannel \
--funding_txid <TXID> \
--output_index <INDEX> \
--psbt
# LND will output a base64 PSBT string; paste it into BitBoxApp's PSBT signing dialog
# after signing, finalize:
lncli --rpcserver=localhost:10009 wallet finalizepsbt \
--funded_psbt "<SIGNED_PSBT_FROM_BITBOXAPP>"
What stays on the node and why that matters
Not nothing. The node has the channel keys, the Lightning node identity key, the routing-fee policy database, and the per-channel state. The node needs to operate continuously to keep channels alive; if the node is offline and the channel peer broadcasts a stale state, the watchtower (if configured) defends, but the node-offline window is when the attack is possible.
The channel keys are derived from the master seed via the BIP-32 derivation hierarchy. The node holds the derived private keys for the channels; it does not hold the master seed. The distinction matters: a compromised node can operate the channels (drain, force-close), but the master seed remains safe on the hardware wallet, and the operator retains the recovery authority.
Caveat: the hardware wallet does not protect channel state. This is the most common misunderstanding. The hardware wallet protects the master seed, which means it protects on-chain recovery authority. It does not protect the current channel state. If the node’s /home/lnd/.lnd/data/graph/ directory is corrupted or deleted without a recent SCB, you lose the channel-specific revocation keys, which prevents cooperative recovery. The seed alone is insufficient for channel recovery; you need the seed plus the latest SCB.
Why LND rather than Core Lightning (CLN)? At the time of writing (May 2026), LND’s createwatchonlywallet RPC and PSBT tooling have better hardware wallet support than CLN’s commando + hsmd path for this use case. CLN’s hsmtool is more powerful for key-surgery scenarios (for example, recovering from a bad HSM state with lnhsm-manipulate or importing keys manually), but the day-to-day watch-only flow is smoother on LND with BitBoxApp. The lncli wallet importpubkey command, introduced in LND 0.16.0, covers cases where you want to import individual addresses rather than a full xpub.
Why watch-only over a hot wallet? A hot wallet, which means a wallet where the node holds the full spending keys, is the simpler configuration. The watch-only setup costs two minutes per on-chain operation for the PSBT signing round-trip. The trade-off: the hot wallet means a compromised node is a total loss of on-chain funds. The watch-only wallet means a compromised node loses channel liquidity (in-flight HTLCs, channel balances) but not the on-chain recovery authority. For a node holding more than 1 million sats on-chain, the two-minute cost per transaction is worth it.
If you need to recover from a situation where the node disk is wiped entirely, the procedure with lncli and the hardware wallet looks like this:
# 1. Fresh LND install, same version (0.18.4-beta as of this writing)
# 2. Re-create the watch-only wallet from the xpub (same createwatchonlywallet command above)
# 3. Restore the SCB to trigger channel force-closes from peers
lncli --rpcserver=localhost:10009 restorechanbackup \
--multi_file /path/to/channel.backup.20260520-120000
# LND will contact each peer and request cooperative closes using the SCB data
# Peers have 2016 blocks (approximately 14 days) to respond before you can force-close
Tested this recovery path on a sacrificial node in April 2026 with 3 channels totaling 500,000 sats. All 3 closed cooperatively within 10 minutes.
The three mistakes
Mistake 1: mixing up derivation paths. The first attempt configured LND with the xpub for m/84'/0'/0', but the node had been initialized expecting m/49'/0'/0' (legacy SegWit-wrapped). The addresses LND generated did not match the addresses the BitBoxApp signed for, and the first transaction was unrecoverable until I rebuilt the wallet configuration with consistent paths.
- Verify which path the node expects at initialization time, before generating keys.
- Export the xpub from BitBoxApp for the exact same path (not the default, unless you confirmed the default matches).
- Document the path in
/home/lnd/wallet-config.txtalongside the wallet birthday timestamp. - Re-derive a test address in both tools and confirm they match before committing funds.
Derivation paths are not interchangeable. Pick native SegWit m/84'/0'/0' for new setups, use the same path everywhere, and document the choice in writing.
Mistake 2: exposing the seed during setup. During the initial setup, I generated the seed on the BitBox and immediately typed it into a backup-verification field on a laptop screen. The seed touched a network-connected machine. Even though the machine was not compromised at the time, the safe assumption is that a seed that has touched a network-connected machine is potentially exposed.
The fix: I wiped the wallet, regenerated a new seed on the BitBox, did not type it into anything network-connected, and treated the original as a tainted seed not to be used for real funds.
The hardware wallet’s own display is the verification surface, which means there is no legitimate reason to type the seed into any software. If a tool asks you to type the seed words into a text field, that is a red flag, not a normal setup step.
Mistake 3: not testing the recovery procedure. I had the seed phrase written down on steel and stored offline. I assumed the recovery would work. The first time I actually tested recovery (on a sacrificial node with a tiny channel), I discovered that I had not actually documented which derivation path the channels used, so the recovered wallet did not see the channel funds.
The fix: a documented recovery procedure that includes the derivation paths, the wallet configuration, and a step-by-step rebuild from seed. The test on a sacrificial node is the verification.
Concretely: I now run a quarterly recovery drill on a second machine with a small amount (100,000 sats) to verify the entire chain works end-to-end. The last drill was in April 2026. Without the drill, an untested recovery procedure is not a recovery procedure.
Where this fits
For the broader Lightning context, see The Operator’s Guide to Self-Hosted Lightning. For the day-one Alby ↗ Hub setup, see Setup: Alby Hub ARM64 Self-Hosted Lightning. For the hardware-wallet setup, see Setup: BitBox Hardware Wallet. For the broader sovereignty argument, see What Sovereign Actually Means in 2026.
subscribe for the recovery-drill walkthrough
The follow-up article documents the quarterly recovery drill: the procedure that verifies the seed-phrase backup actually restores a working node. Subscribe via the footer.