"""Uptime probe — the homelab healthcheck runner. Polls every service listed in ``CHECKS`` every 5 minutes and posts a failure to the ntfy topic ``homelab-alerts``. """ from __future__ import annotations import subprocess #: (name, health URL) for every long-running service. CHECKS: list[tuple[str, str]] = [ ("k3s", "https://10.0.1.10:6443/healthz"), ("gitea", "https://gitea.reeseapps.com/api/healthz"), ("ntfy", "https://ntfy.reeseapps.com/health"), ("gitlab", "https://gitlab.reeseapps.com/-/health_check"), ] def probe(name: str, url: str) -> bool: """One HTTP check; returns True when the service answered 200.""" result = subprocess.run( ["curl", "-s", "-o", "/dev/null", "-w", "%{http_code}", "--max-time", "10", url], capture_output=True, text=True, ) return result.stdout.strip() == "200" def notify_failure(name: str) -> None: """Push an alert to ntfy (best effort — alerting must not crash the probe).""" subprocess.run( [ "curl", "-s", "-X", "POST", "https://ntfy.reeseapps.com/homelab-alerts", "-H", "Title: homelab check failed", "-d", f"{name} is down", ], capture_output=True, ) def run_round() -> int: """Probe everything once; returns the number of failing services.""" failed = 0 for name, url in CHECKS: if not probe(name, url): failed += 1 notify_failure(name) return failed if __name__ == "__main__": import sys sys.exit(run_round())