#!/usr/bin/env bash # ============================================================================= # Timmy node — start all services # Safe to run multiple times; won't double-start a running service. # ============================================================================= set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" SECRETS_FILE="$SCRIPT_DIR/.node-secrets" LND_DIR="$HOME/.lnd" LOG_DIR="$HOME/Library/Logs/timmy-node" LAUNCHD_DIR="$HOME/Library/LaunchAgents" LNCLI="/opt/homebrew/bin/lncli" [[ -f "$SECRETS_FILE" ]] && source "$SECRETS_FILE" mkdir -p "$LOG_DIR" GREEN='\033[0;32m'; CYAN='\033[0;36m'; YELLOW='\033[1;33m'; NC='\033[0m' info() { echo -e "${CYAN}[start]${NC} $*"; } ok() { echo -e "${GREEN}[ok]${NC} $*"; } warn() { echo -e "${YELLOW}[warn]${NC} $*"; } start_launchagent() { local label="$1" local plist="$LAUNCHD_DIR/$label.plist" if ! launchctl list | grep -q "$label"; then launchctl load -w "$plist" 2>/dev/null && ok "$label started." || warn "Could not load $label." else ok "$label is already loaded." fi } # ─── Bitcoin Core ───────────────────────────────────────────────────────────── info "Starting Bitcoin Core…" start_launchagent "com.timmy.bitcoind" # Wait for RPC to be ready (up to 60s) info "Waiting for bitcoind RPC (up to 60s)…" BTC_ARGS=() [[ -n "${RPC_USER:-}" ]] && BTC_ARGS+=(-rpcuser="$RPC_USER" -rpcpassword="$RPC_PASS") for i in $(seq 1 12); do if /opt/homebrew/bin/bitcoin-cli "${BTC_ARGS[@]}" getblockchaininfo &>/dev/null 2>&1; then ok "bitcoind RPC ready." break fi [[ $i -eq 12 ]] && warn "bitcoind RPC not ready after 60s — it may still be starting." || sleep 5 done # ─── LND ────────────────────────────────────────────────────────────────────── info "Starting LND…" start_launchagent "com.timmy.lnd" # Wait for LND to be ready (up to 60s) info "Waiting for LND (up to 60s)…" for i in $(seq 1 12); do LND_STATE=$("$LNCLI" --lnddir="$LND_DIR" state 2>/dev/null | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('state','?'))" 2>/dev/null || echo "?") case "$LND_STATE" in RPC_ACTIVE|SERVER_ACTIVE) ok "LND is up (state: $LND_STATE)." break ;; WALLET_NOT_CREATED) warn "LND wallet needs to be created:" echo " lncli --lnddir=$LND_DIR create" echo " Use password from $SECRETS_FILE" break ;; LOCKED) info "LND wallet is locked — unlocking…" if [[ -f "$LND_DIR/.wallet-password" ]]; then "$LNCLI" --lnddir="$LND_DIR" unlock --stdin < "$LND_DIR/.wallet-password" 2>/dev/null && ok "Wallet unlocked." || warn "Auto-unlock failed — run: lncli --lnddir=$LND_DIR unlock" else warn "No wallet-password file found. Run: lncli --lnddir=$LND_DIR unlock" fi break ;; *) [[ $i -eq 12 ]] && warn "LND not ready after 60s (state: $LND_STATE)" || sleep 5 ;; esac done # ─── LNbits ─────────────────────────────────────────────────────────────────── info "Starting LNbits…" start_launchagent "com.timmy.lnbits" info "Waiting for LNbits HTTP (up to 30s)…" for i in $(seq 1 6); do if curl -sf "http://127.0.0.1:5000/api/v1/health" &>/dev/null; then ok "LNbits is up at http://127.0.0.1:5000" break fi [[ $i -eq 6 ]] && warn "LNbits not responding after 30s — check $LOG_DIR/lnbits.err" || sleep 5 done echo "" ok "All services started. Run 'bash $SCRIPT_DIR/status.sh' for a full health check." echo "" echo " To expose LNbits to Replit, run:" echo " bash $SCRIPT_DIR/expose.sh" echo ""