diff --git a/infrastructure/lnd-init.sh b/infrastructure/lnd-init.sh index 82d7281..428a0e2 100755 --- a/infrastructure/lnd-init.sh +++ b/infrastructure/lnd-init.sh @@ -136,6 +136,44 @@ else 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}" diff --git a/infrastructure/ops.sh b/infrastructure/ops.sh index ce70a54..0b0c528 100755 --- a/infrastructure/ops.sh +++ b/infrastructure/ops.sh @@ -109,6 +109,37 @@ case "${1:-help}" in echo " scp root@:$BACKUP_FILE ./lnd-backup.tar.gz" ;; + sweep) + SWEEP_CONF="$INFRA_DIR/sweep.conf" + SWEEP_LOG="/var/log/timmy-sweep.log" + + echo -e "\n${CYAN}── Sweep config ──────────────────────────────${NC}" + if [[ -f "$SWEEP_CONF" ]]; then + grep -v '^#' "$SWEEP_CONF" | grep -v '^$' || echo "(empty)" + else + echo -e "${YELLOW}No sweep.conf found — sweep is disabled${NC}" + echo " To enable: run lnd-init.sh, or create $SWEEP_CONF manually" + fi + + echo -e "\n${CYAN}── Current on-chain balance ──────────────────${NC}" + docker exec lnd lncli --network=mainnet walletbalance 2>/dev/null \ + | jq '{confirmed_balance, unconfirmed_balance}' \ + || echo "LND not ready" + + echo -e "\n${CYAN}── Last 5 sweep log entries ──────────────────${NC}" + if [[ -f "$SWEEP_LOG" && -s "$SWEEP_LOG" ]]; then + tail -5 "$SWEEP_LOG" + else + echo " No sweep activity yet" + fi + echo "" + ;; + + run-sweep) + echo -e "${CYAN}Running sweep now...${NC}" + bash "$INFRA_DIR/sweep.sh" + ;; + help|*) echo -e "\n${CYAN}Timmy Node operations:${NC}" echo "" @@ -122,6 +153,8 @@ case "${1:-help}" in echo " bash ops.sh lnbits-key — show LNBITS_URL and API key for Replit" echo " bash ops.sh update — pull latest Docker images" echo " bash ops.sh backup — backup LND channel state" + echo " bash ops.sh sweep — show sweep config, balance, and last sweep log" + echo " bash ops.sh run-sweep — run sweep immediately (outside of cron schedule)" echo "" ;; diff --git a/infrastructure/setup.sh b/infrastructure/setup.sh index c3d6036..7b8a9c7 100755 --- a/infrastructure/setup.sh +++ b/infrastructure/setup.sh @@ -123,13 +123,29 @@ services: - $INFRA_DIR/configs/lnd.conf:/root/.lnd/lnd.conf:ro OVERRIDE -# Copy main docker-compose +# Copy main docker-compose and scripts cp "$SCRIPT_DIR/docker-compose.yml" "$INFRA_DIR/docker-compose.yml" cp "$SCRIPT_DIR/lnd-init.sh" "$INFRA_DIR/lnd-init.sh" -chmod +x "$INFRA_DIR/lnd-init.sh" +cp "$SCRIPT_DIR/sweep.sh" "$INFRA_DIR/sweep.sh" +cp "$SCRIPT_DIR/ops.sh" "$INFRA_DIR/ops.sh" +chmod +x "$INFRA_DIR/lnd-init.sh" "$INFRA_DIR/sweep.sh" "$INFRA_DIR/ops.sh" ok "Configs installed" +# ── 7b. Install cron jobs ───────────────────────────────────── +info "Installing cron jobs (daily sweep + daily backup)..." +# Remove any existing Timmy cron entries before re-adding +crontab -l 2>/dev/null | grep -v "timmy-node" | crontab - || true +(crontab -l 2>/dev/null; cat <<'CRON' +# Timmy Node — auto-sweep hot wallet to cold storage (3am UTC daily) +0 3 * * * bash /opt/timmy-node/sweep.sh >> /var/log/timmy-sweep.log 2>&1 +# Timmy Node — LND channel state backup (4am UTC daily) +0 4 * * * bash /opt/timmy-node/ops.sh backup >> /var/log/timmy-backup.log 2>&1 +CRON +) | crontab - +touch /var/log/timmy-sweep.log /var/log/timmy-backup.log +ok "Cron jobs installed (sweep 3am, backup 4am UTC)" + # ── 8. Save credentials ────────────────────────────────────── CREDS_FILE="/root/node-credentials.txt" cat > "$CREDS_FILE" </dev/null \ + | jq -r '.confirmed_balance // "0"') + +if [[ -z "$BALANCE" || "$BALANCE" == "null" ]]; then + log "ERROR — could not read LND wallet balance (is LND running?)" + exit 1 +fi + +log "On-chain balance: ${BALANCE} sats | keep: ${KEEP_SATS} | cold: ${COLD_ADDRESS}" + +# ── Calculate sweep amount ─────────────────────────────────── +SWEEP_AMT=$(( BALANCE - KEEP_SATS )) + +if (( SWEEP_AMT < MIN_SWEEP )); then + log "SKIP — sweep amount ${SWEEP_AMT} sats is below MIN_SWEEP ${MIN_SWEEP} sats (nothing sent)" + exit 0 +fi + +# ── Send to cold address ───────────────────────────────────── +log "SWEEP — sending ${SWEEP_AMT} sats to ${COLD_ADDRESS}..." + +SEND_RESULT=$(docker exec lnd lncli --network=mainnet sendcoins \ + --addr "$COLD_ADDRESS" \ + --amt "$SWEEP_AMT" \ + 2>&1) + +TXID=$(echo "$SEND_RESULT" | jq -r '.txid // empty' 2>/dev/null || echo "") + +if [[ -z "$TXID" ]]; then + log "ERROR — sendcoins failed: $SEND_RESULT" + exit 1 +fi + +log "SUCCESS — txid=${TXID} amount=${SWEEP_AMT} sats → ${COLD_ADDRESS}" + +# ── Trigger channel state backup ───────────────────────────── +log "BACKUP — triggering channel state backup after sweep..." +bash "$INFRA_DIR/ops.sh" backup >> "$LOG_FILE" 2>&1 && \ + log "BACKUP — complete" || \ + log "BACKUP — WARNING: backup failed, check ops.sh backup manually" + +exit 0