#!/usr/bin/env bash # ============================================================ # LND wallet initialization + LNbits startup # Run AFTER Bitcoin sync is complete (verificationprogress ~1.0) # Run as root on the droplet: bash /opt/timmy-node/lnd-init.sh # ============================================================ set -euo pipefail RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; CYAN='\033[0;36m'; NC='\033[0m' info() { echo -e "${CYAN}[lnd-init]${NC} $*"; } ok() { echo -e "${GREEN}[ok]${NC} $*"; } warn() { echo -e "${YELLOW}[warn]${NC} $*"; } die() { echo -e "${RED}[error]${NC} $*"; exit 1; } INFRA_DIR="/opt/timmy-node" CREDS_FILE="/root/node-credentials.txt" # ── Check Bitcoin sync ─────────────────────────────────────── info "Checking Bitcoin sync status..." PROGRESS=$(docker exec bitcoin bitcoin-cli -datadir=/home/bitcoin/.bitcoin getblockchaininfo 2>/dev/null | jq -r .verificationprogress || echo "0") info "Chain sync progress: $PROGRESS" if (( $(echo "$PROGRESS < 0.999" | bc -l) )); then warn "Bitcoin is not fully synced yet (progress: $PROGRESS)" warn "LND needs a synced chain to function correctly." read -rp "Continue anyway? (y/N) " CONFIRM [[ "$CONFIRM" != "y" ]] && die "Aborting. Run again when sync is complete." fi # ── Start LND ──────────────────────────────────────────────── info "Starting LND..." cd "$INFRA_DIR" docker compose up -d lnd sleep 5 # ── Wallet init ────────────────────────────────────────────── echo "" echo -e "${CYAN}══════════════════════════════════════════════${NC}" echo -e "${CYAN} LND Wallet Setup${NC}" echo -e "${CYAN}══════════════════════════════════════════════${NC}" echo "" echo -e " Choose one:" echo -e " ${GREEN}c${NC} — Create a NEW wallet (generates a fresh seed phrase)" echo -e " ${YELLOW}r${NC} — Restore from existing 24-word seed" echo "" read -rp " Your choice [c/r]: " WALLET_CHOICE if [[ "$WALLET_CHOICE" == "r" ]]; then info "Restoring wallet from seed..." docker exec -it lnd lncli --network=mainnet create --recovery-window=2500 else info "Creating new wallet..." docker exec -it lnd lncli --network=mainnet create fi echo "" echo -e "${RED}══════════════════════════════════════════════${NC}" echo -e "${RED} CRITICAL: Write down your 24-word seed phrase${NC}" echo -e "${RED} It was shown above. Store it offline, safely.${NC}" echo -e "${RED} This is the ONLY way to recover your funds.${NC}" echo -e "${RED}══════════════════════════════════════════════${NC}" echo "" read -rp " I have written down my seed phrase. Press enter to continue..." # ── Wait for LND to be ready ───────────────────────────────── info "Waiting for LND to be ready..." for i in {1..30}; do STATE=$(docker exec lnd lncli --network=mainnet state 2>/dev/null | jq -r .state || echo "") if [[ "$STATE" == "SERVER_ACTIVE" ]]; then ok "LND is active" break fi echo -n "." sleep 5 done echo "" # ── Get LND pubkey ─────────────────────────────────────────── PUBKEY=$(docker exec lnd lncli --network=mainnet getinfo 2>/dev/null | jq -r .identity_pubkey || echo "") if [[ -n "$PUBKEY" ]]; then ok "LND node pubkey: $PUBKEY" echo "LND_PUBKEY=$PUBKEY" >> "$CREDS_FILE" fi # ── Start LNbits ───────────────────────────────────────────── info "Starting LNbits..." docker compose up -d lnbits sleep 8 # ── Wait for LNbits ────────────────────────────────────────── for i in {1..20}; do if curl -sf http://127.0.0.1:5000/api/v1/health &>/dev/null; then ok "LNbits is up" break fi echo -n "." sleep 3 done echo "" # ── Enable Tailscale Funnel for LNbits ─────────────────────── info "Exposing LNbits via Tailscale Funnel (public HTTPS)..." tailscale serve --bg http://127.0.0.1:5000 tailscale funnel --bg 443 TAILSCALE_HOSTNAME=$(tailscale status --json | jq -r '.Self.DNSName' | sed 's/\.$//') LNBITS_URL="https://$TAILSCALE_HOSTNAME" ok "LNbits is available at: $LNBITS_URL" echo "LNBITS_URL=$LNBITS_URL" >> "$CREDS_FILE" # ── Create Timmy wallet in LNbits ──────────────────────────── info "Creating Timmy wallet in LNbits..." sleep 3 # Create a user + wallet via LNbits API LNBITS_ADMIN_KEY=$(curl -sf http://127.0.0.1:5000/api/v1/wallets \ -H "Content-Type: application/json" \ | jq -r '.[0].adminkey' 2>/dev/null || echo "") if [[ -z "$LNBITS_ADMIN_KEY" ]]; then warn "Could not auto-create wallet. Open $LNBITS_URL in browser," warn "create a wallet called 'Timmy', and copy the Invoice Key." else # Create a dedicated Timmy wallet TIMMY_WALLET=$(curl -sf http://127.0.0.1:5000/api/v1/account \ -X POST \ -H "Content-Type: application/json" \ -H "X-Api-Key: $LNBITS_ADMIN_KEY" \ -d '{"name":"Timmy"}' 2>/dev/null || echo "{}") TIMMY_INVOICE_KEY=$(echo "$TIMMY_WALLET" | jq -r '.wallets[0].inkey' 2>/dev/null || echo "") if [[ -n "$TIMMY_INVOICE_KEY" ]]; then ok "Timmy wallet created" echo "LNBITS_API_KEY=$TIMMY_INVOICE_KEY" >> "$CREDS_FILE" fi fi # ── Configure cold storage sweep ──────────────────────────── echo "" echo -e "${CYAN}══════════════════════════════════════════════${NC}" echo -e "${CYAN} Cold Storage Auto-Sweep Setup${NC}" echo -e "${CYAN}══════════════════════════════════════════════${NC}" echo "" echo -e " Excess on-chain sats above your threshold are automatically" echo -e " sent to a cold address daily. You never touch the hot wallet." echo "" echo -e " Enter your cold Bitcoin address (hardware wallet, Sparrow, etc.)" echo -e " Press Enter to skip — you can configure this later by editing" echo -e " $INFRA_DIR/sweep.conf" echo "" read -rp " Cold address (bc1q... / 1... / 3...): " COLD_ADDRESS SWEEP_CONF="$INFRA_DIR/sweep.conf" if [[ -n "$COLD_ADDRESS" ]]; then cat > "$SWEEP_CONF" <> $INFRA_DIR/sweep.conf" fi # ── Final output ───────────────────────────────────────────── echo "" echo -e "${GREEN}════════════════════════════════════════════════${NC}" echo -e "${GREEN} LND + LNbits ready — set these in Replit:${NC}" echo -e "${GREEN}════════════════════════════════════════════════${NC}" echo "" grep "LNBITS_URL\|LNBITS_API_KEY" "$CREDS_FILE" 2>/dev/null || true echo "" echo -e " If LNBITS_API_KEY is blank above, open $LNBITS_URL," echo -e " create a wallet called 'Timmy', and copy its Invoice Key." echo "" echo -e " ${CYAN}Next: fund your Lightning node${NC}" echo -e " Get your on-chain address:" echo -e " docker exec lnd lncli --network=mainnet newaddress p2wkh" echo -e " Send at least 0.001 BTC to open your first channel." echo "" echo -e " Full credentials: ${YELLOW}cat $CREDS_FILE${NC}" echo ""