Skip to content

Sensitive tier: ntfy, to bring the notification path in-house #136

Closed

Description

@Gerrrt

Sub-issue of #102.

What and why

ntfy Apache-2.0/GPL-2.0 Go/Docker/K8S — awesome-selfhosted README.md:355.

The alerting path already assumes ntfy. docs/roadmap.md records under Done that Alertmanager pointed at the ntfy.example.invalid placeholder for the entire life of the stack, so no alert had ever been delivered, and that it now points at a real receiver. #67 added a dead man's switch on top of it (PR #119), and #120 is waiting on the secrets to deploy that.

This issue is the last piece: run the receiver rather than depend on someone else's.

The one thing to think about first

#67 identified the constraint that matters here:

The awkward part is that the watcher has to live somewhere other than this host, or it fails at the same time as the thing it is watching.

Self-hosting ntfy pulls the notification path closer to the thing it reports on. Today prometheus (VLAN 99) alerts through a service that is not in the house; afterwards it would alert through a service on the same segment, on a box that is one of two shelf laptops behind a switch with no battery (#110).

That is not an argument against doing it — a mains cut already takes the alerting host down, so the marginal loss is small. But it means:

  • The dead man's switch watcher must stay external, whatever else moves in-house. A self-hosted ntfy watched by a self-hosted checker is a closed loop that reports success right up until both are off.
  • Or keep ntfy.sh as the delivery path for the watchdog specifically, and self-host everything else. Worth deciding explicitly rather than by omission.

What it needs

  • Auth configured — an ntfy instance with open publish is an open relay for anything that can reach it.
  • Topic names and any token in secrets/*.sops.yaml, replacing the values Fill in the three new notification secrets, then deploy #118 and #119 #120 is currently waiting on.
  • Alertmanager's receiver URL repointed, and the alert path re-verified end to end afterwards, per docs/runbooks/verify-the-alert-path.md. The lesson from the placeholder is that a notification path is only believed after it has been observed working — a 200 is not evidence.
  • Phones need the app pointed at the internal instance, which only works on-network given there is no external exposure.

Which VLAN this touches

🔴 99 — Winterfell (management)

Security review

  • Does not weaken the default-deny posture between VLANs — publishers are on 99 and 50, both already permitted; no new rule
  • Any new credential goes in secrets/*.sops.yaml — and note docs/security.md records a webhook URL previously found in a session transcript and rotated; treat topic URLs as credentials
  • Any new device is added to docs/network.md with an OUI-truncated MAC — n/a

Depends on

#129 (Caddy) and the mini PC in #102. Interacts with #120 (secrets pending), #119/#67 (dead man's switch), #110 (the shelf switch has no battery).


Corrected 2026-09-20

Three premises above are spent. #120 closed 2026-09-04 (the receivers are filled in and alerts deliver); #110 closed 2026-09-08 (the shelf switch is racked in U4 on UPS power, so "a switch with no battery" is gone); and the ntfy.sh quota was found to be spent daily by the heartbeat (#407), which moved the dead man's switch to healthchecks.io on 2026-09-09 (#408). That last one answers the question this issue said to decide explicitly: the watcher is already external and stays external; what an in-house ntfy would replace is the delivery path for alerts, not the heartbeat. "Depends on #102" → #404. Not authored, and it is seq/7 in trinity on purpose — it has nothing to publish to until the tier serves.

Activity

  1. added this to the Wave 1 — trinity milestone on Sep 11, 2026
  2. added
    seq/7Step 7 within its milestone; same number = can run in parallel
    on Sep 11, 2026
  3. Gerrrt commented on Sep 29, 2026

    @Gerrrt
    OwnerAuthor

    Closed by #722, after the cutover was run end to end on 2026-09-28/29. The times are in verify-the-alert-path.md.

    Decision: all three real channels go to the in-house ntfy on trinity. urgent and security also go to ntfy.sh, because a phone off the home network can't reach the tier. The heartbeat stays on healthchecks.io.

    Observed:

    • A synthetic page arrived in-house on Wi-Fi, and on the ntfy.sh copy over mobile data.
    • Kill drill: docker stop sensitive-ntfy at 03:12:05, and EndpointUnreachable paged through ntfy.sh at 03:18.
    • After the restart, Alertmanager's retries delivered the held-back in-house pages.

    Delivered: #712 (service and repoint), #715 (probe), #716 and #719 (title-and-a-line rendering on both copies), #717 and #720 (encrypted secrets), #718 (seven-day ACME leaves off the estate expiry rules).

    Not run: the runbook's step 8 second half. Neither the lowered Watchdog route (daily route to the in-house alerts topic only) nor a category=security page has been observed. Both are cheap to do the next time someone is at the monitoring host.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requestsensitivestacks/sensitive on trinity (VLAN 99)seq/7Step 7 within its milestone; same number = can run in parallel

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions