#!/usr/bin/env bash # Install ntfy (https://ntfy.sh) as the public notification server for the # Ping River Monitor. Run as root on the monitor VPS. Idempotent. # # NTFY_DOMAIN=ntfy.buildfor.life bash scripts/install_ntfy.sh # # What it does: # - installs the ntfy .deb from the official GitHub release (single Go # binary, ~30 MB RSS, sqlite message cache) # - writes /etc/ntfy/server.yml: listens on the Tailscale address only # (the reverse proxy is another VPS on the tailnet; nothing is exposed # on a public interface), anonymous READ on all topics, WRITE only with # a token. Override with NTFY_LISTEN=host:port. # - creates the `monitor` publishing user + token, writes NTFY_SERVER / # NTFY_TOKEN into /opt/thailand-water-monitor/.env if not present # # Reverse proxy (on the Caddy VPS, over Tailscale): # ntfy.buildfor.life { # reverse_proxy :2586 # } # Caddy passes websockets and keeps long-poll connections open by default; # subscribers hold one open. ntfy runs with behind-proxy: true so rate # limits key on X-Forwarded-For, not on the proxy's address. set -euo pipefail NTFY_DOMAIN="${NTFY_DOMAIN:?set NTFY_DOMAIN, e.g. ntfy.buildfor.life}" NTFY_VERSION="${NTFY_VERSION:-2.28.0}" MONITOR_DIR="${MONITOR_DIR:-/opt/thailand-water-monitor}" TS_IP="$(tailscale ip -4 2>/dev/null | head -1 || true)" LISTEN="${NTFY_LISTEN:-${TS_IP:-127.0.0.1}:2586}" echo "ntfy will listen on ${LISTEN}" if ! command -v ntfy >/dev/null || [[ "$(ntfy --version 2>/dev/null | awk '{print $3}')" != "$NTFY_VERSION" ]]; then tmp=$(mktemp -d) curl -fsSL -o "$tmp/ntfy.deb" \ "https://github.com/binwiederhier/ntfy/releases/download/v${NTFY_VERSION}/ntfy_${NTFY_VERSION}_linux_amd64.deb" dpkg -i "$tmp/ntfy.deb" rm -rf "$tmp" fi install -d -m 755 /var/cache/ntfy /var/lib/ntfy cat > /etc/ntfy/server.yml </dev/null && echo "ntfy up on ${LISTEN}" # Publishing identity for the monitor if ! ntfy user list 2>/dev/null | grep -q '^user monitor (role'; then NTFY_PASSWORD="$(openssl rand -base64 24)" ntfy user add --role=user monitor fi ntfy access monitor 'ping-*' write-only >/dev/null # 'ping-*' read stays anonymous via auth-default-access token=$(ntfy token list monitor 2>/dev/null | awk '/^- tk_/{print $2; exit}') # '- tk_xxx (label), ...' if [[ -z "$token" ]]; then token=$(ntfy token add --label "water-monitor" monitor | grep -oE 'tk_[A-Za-z0-9]+' | head -1) # 'token tk_xxx created for user monitor' fi env_file="${MONITOR_DIR}/.env" if [[ -f "$env_file" ]] && ! grep -q '^NTFY_SERVER=' "$env_file"; then { echo "" echo "# ntfy public notifications (scripts/install_ntfy.sh)" echo "NTFY_SERVER=https://${NTFY_DOMAIN}" echo "NTFY_PUBLISH_URL=http://${LISTEN}" echo "NTFY_TOPIC_PREFIX=ping" echo "NTFY_TOKEN=${token}" } >> "$env_file" echo "wrote NTFY_* to ${env_file}; restart water-monitor to enable" else echo "NTFY_TOKEN=${token}" fi echo echo "Subscribe test (anonymous read): curl -s 'http://${LISTEN}/ping-status/json?poll=1'" echo "Publish test (needs token): curl -s -H 'Authorization: Bearer ${token}' -d 'hello' http://${LISTEN}/ping-status"