From 906cded369d12401de2fd37455c0369ce9abe4a5 Mon Sep 17 00:00:00 2001 From: oz6un Date: Mon, 20 Jul 2026 10:05:56 +0300 Subject: [PATCH] chore: remove the hourly health-check push notifications The devbox-health systemd timer pushed to Pushover on disk >=85% or a failed unit, but was provably silent in practice (0 failed units, disk 52%) and the user wants no background ops pushes. Removed the template, the systemd service+timer install, the render step, and all README/Makefile references. claude-notify (Claude-is-waiting hook) keeps the Pushover keys. Live box brought to parity (timer disabled, units + script removed). Co-Authored-By: Claude Fable 5 --- Makefile | 2 +- README.md | 7 ++----- files/devbox-health.tmpl | 36 ------------------------------------ files/remote-setup.sh | 24 ------------------------ setup-user.sh | 7 ++----- 5 files changed, 5 insertions(+), 71 deletions(-) delete mode 100644 files/devbox-health.tmpl diff --git a/Makefile b/Makefile index 07a6284..6d0c3f7 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ SCRIPTS = preflight.sh provision.sh setup-user.sh sync-code.sh destroy.sh \ - files/remote-setup.sh files/claude-notify.tmpl files/devbox-health.tmpl + files/remote-setup.sh files/claude-notify.tmpl .PHONY: check residue preflight provision setup sync all destroy diff --git a/README.md b/README.md index 0e3c998..224b6e7 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,6 @@ manage, wired for Claude Code — and rebuild the whole thing from this repo in - **Tailnet-only.** Zero public TCP ports. Access is Tailscale SSH: your tailnet identity is the credential. - **Hardened and self-maintaining.** UFW default-deny, key-only sshd, automatic security patches with a nightly reboot window. -- **Self-alerting.** Pushes to your phone when the disk fills or a service fails. - **Ready for development.** fish + starship, persistent tmux, Node/pnpm, Docker, Claude Code with configurable skills and phone notifications (and OpenAI Codex CLI, optional). - **Instant previews.** `http://devbox:` reaches any dev server or container on the box — even one bound to localhost. @@ -40,8 +39,7 @@ flowchart LR | Sessions | tmux auto-attach on SSH + resurrect/continuum | Survives disconnects *and* the 04:00 patch reboots | | Localhost preview | iptables(-nft) REDIRECT → `tailnet-devproxy.py` (SO_ORIGINAL_DST) | `http://devbox:` works even for servers bound to `127.0.0.1`/`::1` | | Containers | Docker + Compose, publishes default to `127.0.0.1` | Containers stay off the internet (Docker bypasses UFW — see FOOTGUNS); the tailnet reaches them via the devproxy | -| Self-alerting | hourly root timer → Pushover | Pushes only on trouble: disk ≥85% or a failed unit, repeating hourly until fixed | -| Notifications | Claude Code hooks → Pushover | Presence-aware; includes turn-failure alerts | +| Notifications | Claude Code hooks → Pushover | Presence-aware; pushes only when Claude is waiting on you or a turn failed | | Recovery | Hetzner rescue mode / console | No credentials live on the box; reset root via Hetzner if ever needed | ## Prerequisites @@ -99,8 +97,7 @@ Optional: enable **Tailscale Serve** on your tailnet for HTTPS preview URLs — - **`claude` in any repo** pushes to your phone only when it's genuinely waiting on you — idle after handing control back, or blocked on a decision — plus turn failures. It deliberately does *not* ping on every completed turn (that floods during autonomous multi-step work), and stays - quiet while you're active in tmux. A **"devbox health"** push is the hourly monitor flagging low - disk or a failed unit. + quiet while you're active in tmux. ## Rebuild and teardown diff --git a/files/devbox-health.tmpl b/files/devbox-health.tmpl deleted file mode 100644 index ae8df7e..0000000 --- a/files/devbox-health.tmpl +++ /dev/null @@ -1,36 +0,0 @@ -#!/usr/bin/env bash -# Hourly health check -> Pushover. Silent when healthy; one summary push when -# not. Rendered from devbox-health.tmpl by setup-user.sh; installed as a root -# systemd timer by remote-setup.sh. -PO_TOKEN="__PUSHOVER_TOKEN__" -PO_USER="__PUSHOVER_USER__" -DISK_LIMIT=85 - -problems="" - -disk=$(df --output=pcent / | tail -1 | tr -dc '0-9') -if [ "${disk:-0}" -ge "$DISK_LIMIT" ]; then - problems="disk at ${disk}% (limit ${DISK_LIMIT}%)" -fi - -failed=$(systemctl --failed --no-legend --plain | awk '{print $1}' | paste -sd, -) -if [ -n "$failed" ]; then - problems="${problems:+$problems; }failed units: $failed" -fi - -[ -z "$problems" ] && exit 0 -problems=${problems:0:900} # Pushover message cap is 1024; don't let a mass failure 400 - -# The split literal keeps the render sed from rewriting this guard (see FOOTGUNS). -if [ -n "$PO_TOKEN" ] && [ "$PO_TOKEN" != "__PUSHOVER""_TOKEN__" ]; then - # -f: an API rejection must not masquerade as success; the WARN lands in the - # journal so a lost alert is at least diagnosable (unit itself stays green). - curl -fsS -o /dev/null --max-time 10 \ - --form-string "token=$PO_TOKEN" --form-string "user=$PO_USER" \ - --form-string "title=devbox health: $(hostname)" \ - --form-string "message=$problems" \ - --form-string "priority=1" --form-string "sound=siren" \ - https://api.pushover.net/1/messages.json \ - || echo "WARN: pushover send failed for: $problems" >&2 -fi -exit 0 diff --git a/files/remote-setup.sh b/files/remote-setup.sh index ac1bcef..f3581fe 100755 --- a/files/remote-setup.sh +++ b/files/remote-setup.sh @@ -66,30 +66,6 @@ if [ "$(cat "$S/install-codex" 2>/dev/null)" = 1 ]; then || echo "WARN: bubblewrap install failed (codex falls back to a bundled copy)" fi -echo "== health-check timer (root systemd, hourly) ==" -sudo install -m 700 -o root -g root "$S/devbox-health" /usr/local/bin/devbox-health -sudo tee /etc/systemd/system/devbox-health.service >/dev/null <<'UNIT' -[Unit] -Description=Devbox health check -> Pushover -Wants=network-online.target -After=network-online.target -[Service] -Type=oneshot -ExecStart=/usr/local/bin/devbox-health -UNIT -sudo tee /etc/systemd/system/devbox-health.timer >/dev/null <<'UNIT' -[Unit] -Description=Hourly devbox health check -[Timer] -OnCalendar=hourly -RandomizedDelaySec=300 -Persistent=true -[Install] -WantedBy=timers.target -UNIT -sudo systemctl daemon-reload -sudo systemctl enable --now devbox-health.timer >/dev/null - echo "== git identity ==" # Identity arrives as files (see setup-user.sh) so no quoting layer ever parses it. GIT_NAME=$(cat "$S/git-name" 2>/dev/null || true) diff --git a/setup-user.sh b/setup-user.sh index b131067..4f4e332 100755 --- a/setup-user.sh +++ b/setup-user.sh @@ -11,8 +11,8 @@ source ./secrets.env DEVBOX_NAME="${DEVBOX_NAME:-devbox}" DEV_USER="${DEV_USER:-dev}" -# Pushover keys render into a ROOT-executed script (devbox-health); real keys -# are alphanumeric, so enforce exactly that — kills the injection class. +# Pushover keys render (via sed) into the claude-notify hook; real keys are +# alphanumeric, so enforce exactly that — keeps the render clean and safe. for v in PUSHOVER_TOKEN PUSHOVER_USER; do val="${!v:-}" if [ -n "$val" ] && ! echo "$val" | grep -Eq '^[A-Za-z0-9]+$'; then @@ -37,9 +37,6 @@ cp files/config.fish files/tmux.conf files/fnm.fish files/remote-setup.sh "$stag sed -e "s|__PUSHOVER_TOKEN__|${PUSHOVER_TOKEN:-}|g" \ -e "s|__PUSHOVER_USER__|${PUSHOVER_USER:-}|g" \ files/claude-notify.tmpl > "$staging/claude-notify" -sed -e "s|__PUSHOVER_TOKEN__|${PUSHOVER_TOKEN:-}|g" \ - -e "s|__PUSHOVER_USER__|${PUSHOVER_USER:-}|g" \ - files/devbox-health.tmpl > "$staging/devbox-health" sed -e "s|__DEVBOX_NAME__|$DEVBOX_NAME|g" files/vite-hosts.fish > "$staging/vite-hosts.fish" sed -e "s|__DEV_USER__|$DEV_USER|g" files/claude-settings.json > "$staging/claude-settings.json" printf '%s' "${GIT_NAME:-}" > "$staging/git-name"