send (title/priority/tags), read (poll mode, greppable one-liners), topics (known homelab buses from config). Config with homelab defaults on first run. Unit tests against httptest; smoked against the real ntfy on topic agent-tools-test. Spec: doc/tool-parity.md 3.3. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011KikHkfCiC3yELbsMN8fT9
1.8 KiB
notifyr — ntfy client for agents
Send and read notifications on the homelab's ntfy bus
(ntfy.brasse-pc.eu) with one stable command prefix, so any agent can
alert a human — and check what the infrastructure has been complaining
about — without hand-rolled curl loops that each need a fresh
approval. Closes the PushNotification gap from
doc/tool-parity.md §3.3.
Usage
notifyr send --msg "text" [--topic T] [--title X]
[--priority min|low|default|high|urgent] [--tags a,b]
notifyr read [--topic T] [--since 10m|2h|all] [--limit N]
notifyr topics # known topics + which one is the default
notifyr version
Examples:
notifyr send --topic ci-fel --title "Build failed" \
--msg "agent-tools arm64 test: FAIL" --priority high
notifyr read --topic pi5-server-fel --since 2h # what has alerted lately?
notifyr read --limit 5 # newest 5 on the default topic
read uses ntfy's poll mode (/json?poll=1&since=…) and prints one
greppable line per message:
2026-08-07 00:29:40 [high] (Build failed) agent-tools arm64 test: FAIL #warning
Exit codes: 0 ok, 1 error (bad config, server unreachable, invalid
priority…). read with zero messages is not an error.
Config
~/.config/notifyr/config.json, created with homelab defaults on the
first run (NOTIFYR_CONFIG overrides the path):
{
"server": "https://ntfy.brasse-pc.eu",
"token": "",
"default_topic": "claude",
"topics": { "ci-fel": "failed Gitea Actions builds", "…": "…" }
}
topics is informational — it feeds notifyr topics so an agent can
pick the right bus without reading infra-Doc first. Edit freely.
Build & test
go test ./...
go build -o build/notifyr .