diff --git a/scripts/hermes-lnbits/provision.sh b/scripts/hermes-lnbits/provision.sh index 85db105..af44301 100644 --- a/scripts/hermes-lnbits/provision.sh +++ b/scripts/hermes-lnbits/provision.sh @@ -1,32 +1,34 @@ #!/usr/bin/env bash # ============================================================================= # Hermes VPS — LNbits provisioning script -# Target: Ubuntu 24.04 LTS, tested on 143.198.27.163 +# Target: Ubuntu 24.04 LTS (tested on root@143.198.27.163) # # Usage: # export DB_PASS="$(openssl rand -hex 20)" +# export TAILSCALE_IP="$(tailscale ip -4)" # e.g. 100.x.x.x # bash scripts/hermes-lnbits/provision.sh # -# Required environment variables (operator must supply): -# DB_PASS — password for the lnbits PostgreSQL role (generate securely) +# Required env vars (no defaults — operator must supply): +# DB_PASS — password for the lnbits PostgreSQL role +# TAILSCALE_IP — Tailscale IPv4 of this host (Nginx binds only to this) # -# Optional environment variables: -# LNBITS_VERSION (default: 0.12.12) -# LNBITS_PORT (default: 5000) -# LNBITS_DIR (default: /opt/lnbits) -# SERVICE_USER (default: lnbits — dedicated non-root user) +# Backend note: +# FakeWallet is used rather than VoidWallet because FakeWallet settles +# internal (self-payment) invoices in real-time, which is required for +# the dev/test payment simulation loop. VoidWallet silently drops all +# outbound payments and never fires invoice-settled events. # -# After this script completes: -# 1. Open http://:${LNBITS_PORT} in a browser -# 2. Create a wallet and copy the admin key -# 3. Set Replit secrets: -# LNBITS_URL=http://:${LNBITS_PORT} +# After this script: +# 1. Extract the admin key non-interactively (see step below). +# 2. Set Replit secrets: +# LNBITS_URL=http://${TAILSCALE_IP}:5000 # LNBITS_API_KEY= -# 4. Restart the api-server workflow +# 3. Restart the api-server workflow. # ============================================================================= set -euo pipefail : "${DB_PASS:?DB_PASS must be set. Run: export DB_PASS=\$(openssl rand -hex 20)}" +: "${TAILSCALE_IP:?TAILSCALE_IP must be set. Run: export TAILSCALE_IP=\$(tailscale ip -4)}" LNBITS_VERSION="${LNBITS_VERSION:-0.12.12}" LNBITS_PORT="${LNBITS_PORT:-5000}" @@ -38,10 +40,11 @@ DB_USER=lnbits echo "==> Installing system deps" apt-get update -qq apt-get install -y -qq python3.11 python3.11-venv python3-pip \ - postgresql postgresql-contrib nginx curl git openssl + postgresql postgresql-contrib nginx curl git openssl jq -echo "==> Creating service user (non-root)" -id -u "${SERVICE_USER}" &>/dev/null || useradd -r -m -d "${LNBITS_DIR}" -s /usr/sbin/nologin "${SERVICE_USER}" +echo "==> Creating dedicated service user (non-root)" +id -u "${SERVICE_USER}" &>/dev/null || \ + useradd -r -m -d "${LNBITS_DIR}" -s /usr/sbin/nologin "${SERVICE_USER}" echo "==> Configuring PostgreSQL" systemctl enable --now postgresql @@ -51,13 +54,13 @@ sudo -u postgres psql -c "ALTER ROLE ${DB_USER} WITH PASSWORD '${DB_PASS}';" sudo -u postgres psql -tc "SELECT 1 FROM pg_database WHERE datname='${DB_NAME}'" \ | grep -q 1 || sudo -u postgres createdb -O "${DB_USER}" "${DB_NAME}" -echo "==> Creating LNbits venv" +echo "==> Creating LNbits venv at ${LNBITS_DIR}/.venv" mkdir -p "${LNBITS_DIR}/data" python3.11 -m venv "${LNBITS_DIR}/.venv" "${LNBITS_DIR}/.venv/bin/pip" install --quiet --upgrade pip "${LNBITS_DIR}/.venv/bin/pip" install --quiet "lnbits==${LNBITS_VERSION}" psycopg2-binary -echo "==> Writing env file (root-readable only)" +echo "==> Writing ${LNBITS_DIR}/.env (mode 600, owned by ${SERVICE_USER})" cat > "${LNBITS_DIR}/.env" << ENVFILE LNBITS_BACKEND_WALLET_CLASS=FakeWallet HOST=0.0.0.0 @@ -69,19 +72,18 @@ ENVFILE chmod 600 "${LNBITS_DIR}/.env" chown "${SERVICE_USER}:${SERVICE_USER}" "${LNBITS_DIR}/.env" -echo "==> Writing /opt/lnbits/run.sh" -cat > "${LNBITS_DIR}/run.sh" << RUNSH +echo "==> Writing ${LNBITS_DIR}/run.sh" +cat > "${LNBITS_DIR}/run.sh" << 'RUNSH' #!/usr/bin/env bash set -a -source "${LNBITS_DIR}/.env" +source /opt/lnbits/.env set +a -exec "${LNBITS_DIR}/.venv/bin/lnbits" --host 0.0.0.0 --port "${LNBITS_PORT}" +exec /opt/lnbits/.venv/bin/lnbits --host 0.0.0.0 --port "${PORT}" RUNSH chmod 750 "${LNBITS_DIR}/run.sh" -chown "${SERVICE_USER}:${SERVICE_USER}" "${LNBITS_DIR}/run.sh" chown -R "${SERVICE_USER}:${SERVICE_USER}" "${LNBITS_DIR}" -echo "==> Writing systemd unit" +echo "==> Installing systemd unit (User=${SERVICE_USER}, hardened)" cat > /etc/systemd/system/lnbits.service << UNIT [Unit] Description=LNbits Lightning wallet server @@ -110,17 +112,16 @@ systemctl restart lnbits echo "==> Waiting for LNbits to start (20 s)..." sleep 20 -echo "==> Switching backend to FakeWallet via SQL" -# FakeWallet settles internal self-payments — required for dev/test payment simulation. -# VoidWallet silently drops all payments and cannot settle invoices. +echo "==> Ensuring FakeWallet is set in DB" sudo -u postgres psql "${DB_NAME}" -c \ "INSERT INTO system_settings (id, value) VALUES ('lnbits_backend_wallet_class', '\"FakeWallet\"') ON CONFLICT (id) DO UPDATE SET value = EXCLUDED.value;" 2>/dev/null || true -echo "==> Writing Nginx reverse-proxy config" +echo "==> Configuring Nginx (Tailscale-only listener on ${TAILSCALE_IP})" cat > /etc/nginx/sites-available/lnbits << NGINX +# Bind only to the Tailscale interface — not exposed on public IP server { - listen 80; + listen ${TAILSCALE_IP}:80; server_name _; location / { @@ -142,18 +143,27 @@ echo "==> Health check" if HEALTH=$(curl -sf "http://localhost:${LNBITS_PORT}/api/v1/health" 2>&1); then echo " LNbits health: ${HEALTH}" else - echo " WARNING: health check failed — check: journalctl -u lnbits -n 100" - journalctl -u lnbits -n 20 --no-pager + echo " WARNING: health check failed" + journalctl -u lnbits -n 30 --no-pager + exit 1 +fi + +echo "==> Extracting admin key (non-interactive)" +# LNbits >= 0.12 creates a superuser on first start. Query the DB directly. +ADMIN_KEY=$(sudo -u postgres psql "${DB_NAME}" -tAc \ + "SELECT adminkey FROM wallets WHERE deleted = false LIMIT 1;" 2>/dev/null || true) +if [[ -n "${ADMIN_KEY}" ]]; then + echo " Admin key: ${ADMIN_KEY}" + echo " Set in Replit: LNBITS_API_KEY=${ADMIN_KEY}" +else + echo " No wallet found yet — open LNbits UI to create one, then run:" + echo " sudo -u postgres psql lnbits -c \"SELECT adminkey FROM wallets LIMIT 1;\"" fi systemctl status lnbits --no-pager | head -8 echo "" -echo "==> DONE" -echo " LNbits: http://$(curl -sf https://api.ipify.org 2>/dev/null || echo ''):${LNBITS_PORT}" -echo " Next:" -echo " 1. Open LNbits UI, create a wallet, copy admin key" -echo " 2. Set Replit secrets:" -echo " LNBITS_URL=http://:${LNBITS_PORT}" -echo " LNBITS_API_KEY=" -echo " 3. Restart api-server workflow" +echo "==> DONE. Set these Replit secrets:" +echo " LNBITS_URL=http://${TAILSCALE_IP}:80" +echo " LNBITS_API_KEY=${ADMIN_KEY:-}" +echo " Then restart the api-server workflow."