# lazyntfy A [lazygit](https://github.com/jesseduffield/lazygit)-style terminal client for [ntfy](https://ntfy.sh): topics in the left pane, messages in the middle pane, and the selected message in the right pane. ``` ╭ topics ─────╮ ╭ messages ─────────────────╮ ╭ detail ───────────────╮ │ ● alerts 12 │ │ 06-14 09:21 CRIT DiskFull │ │ CRITICAL │ │ ○ backups │ │ 06-14 09:03 high Backup… │ │ DiskFull │ │ ○ deploy │ │ 06-13 22:47 def Deploy… │ │ 2026-06-14 09:21:07 │ │ │ │ │ │ mK3n9Qx… │ │ │ │ │ │ │ ╰─────────────╯ ╰────────────────────────────╯ ╰───────────────────────╯ lazyntfy alerts 12/12 ● connected ? help q quit ``` lazyntfy talks to the ntfy HTTP API directly (`GET /topic/json` to subscribe, `POST /topic` to publish). It reads `~/.config/ntfy/client.yml` for credentials and subscriptions, groups topics that share a server and credentials onto one stream, and reconnects automatically — resuming from the newest event it has already seen so nothing is delivered twice. ## Features - **lazygit-style layout** — topics, messages, and message detail in three panes. - **Account discovery** — with no topic on the command line it lists your subscriptions, reservations, and ACL grants. - **Alert-aware** — Grafana and Alertmanager webhook bodies are summarized (status, alert name, severity, summary); `v` shows the raw JSON. - **Persistent** — topics you add and the messages that arrive are stored in a small SQLite file and restored on the next start. - **Resilient** — reconnects with exponential backoff, honoring `message_delete` and `message_clear` events. - **Single binary, no CGo** — pure Go, including the SQLite driver. ## Requirements - Go 1.26 or newer (to build) - A reachable ntfy server ## Installation ### Arch Linux A `PKGBUILD` lives in [`packaging/archlinux`](packaging/archlinux). Build and install the package with `makepkg`: ```sh git clone https://src.panahifar.ir/ahp/lazyntfy.git cd lazyntfy/packaging/archlinux makepkg -si ``` This installs `/usr/bin/lazyntfy`, the man page under `/usr/share/man`, the license, and the README, and pulls in `ca-certificates` so TLS to your ntfy server works. ### Debian and Ubuntu A debhelper-free source package lives in [`debian`](debian). Build a `.deb` and install it system-wide: ```sh git clone https://src.panahifar.ir/ahp/lazyntfy.git cd lazyntfy make deb # writes dist/lazyntfy__.deb sudo apt install ./dist/lazyntfy_*.deb ``` `apt install ./file.deb` (or `dpkg -i` followed by `apt -f install`) resolves the `ca-certificates` dependency. The packaging needs only the Go toolchain and `dpkg-dev`; `make deb` passes `-d` so it also builds where `golang-go` is not installed as a distribution package. ### From source ```sh git clone https://src.panahifar.ir/ahp/lazyntfy.git cd lazyntfy make install # into ~/.local, no root needed ``` `make install` honors `PREFIX`, `BINDIR`, `MANDIR`, `DOCDIR`, and `DESTDIR`. Picking a system prefix installs for every user: ```sh sudo make install PREFIX=/usr/local # /usr/local/bin/lazyntfy, /usr/local/share/man/man1 sudo make install PREFIX=/usr # /usr/bin/lazyntfy, /usr/share/man/man1 ``` Stage a filesystem tree into a temporary root instead of the live system (handy for building your own package): ```sh make install DESTDIR="$PWD/stage" PREFIX=/usr ``` ### With `go install` From a checkout: ```sh go install ./cmd/lazyntfy # binary in $(go env GOPATH)/bin ``` ## Usage ```sh lazyntfy # topics from the account, their history, and client.yml lazyntfy alerts # one topic, expanded with default-host lazyntfy --since 1h alerts # replay the last hour lazyntfy --host https://ntfy.example alerts lazyntfy --token tk_xxx alerts lazyntfy --user name:pass alerts ``` With no topic on the command line, lazyntfy asks the server for your account subscriptions, reserved topics, and ACL grants (`GET /v1/account` and, when allowed, `GET /v1/users`), then adds the topics remembered from previous sessions. You can always add a topic for the current session with `a`. ## Key bindings Keys act on the focused pane. Use `tab` / `shift-tab` or `1` / `2` / `3` to change panes. | Key | Action | | --- | --- | | `j` / `k`, arrows | move in the focused pane | | `g` / `G` | first / last | | `ctrl-u` / `ctrl-d` | page up / down | | `1` / `2` / `3` | focus topics, messages, detail | | `tab` / `shift-tab` | next / previous pane | | `enter` | topics → messages → detail | | `esc` | back from detail, or clear the filter | | `/` | filter title, body, tags, and id | | `r` | reconnect | | `p` | publish to the focused topic | | `a` | subscribe to another topic for this session | | `v` | toggle raw JSON | | `o` | open the message click URL | | `?` | help | | `q` | quit | On a narrow terminal the detail pane is hidden until it is focused. ## Configuration lazyntfy reads the official ntfy client configuration at `~/.config/ntfy/client.yml`: ```yaml default-host: https://ntfy.example default-token: tk_xxxxxxxxxxxxxxxx subscribe: - topic: alerts - topic: https://other.example/backups user: alice password: secret ``` Positional topics on the command line replace the file's `subscribe` list. Credentials are sent in the `Authorization` header and are never printed or written back. See `man lazyntfy` for every flag. ## History and storage Topics you add and the messages that arrive are kept in a small SQLite database at `$XDG_DATA_HOME/lazyntfy/history.db` (`~/.local/share/lazyntfy/history.db` by default), so the last session is restored on the next start. Each topic keeps its newest 1000 messages; expired messages are pruned on startup. Credentials are never written to the database — the current run's authentication is always used. History is best effort: if the file cannot be opened, the client still runs. ## Packaging Both package formats are defined in the repository. `make deb` builds a Debian package from the working tree; the Arch `PKGBUILD` builds the `v$pkgver` release archive from the Gitea instance. | Format | Build | Definition | | --- | --- | --- | | Debian / Ubuntu | `make deb` → `dist/*.deb` | [`debian/`](debian) | | Arch Linux | `make arch` → `makepkg` | [`packaging/archlinux/PKGBUILD`](packaging/archlinux/PKGBUILD) | When cutting a release: 1. Bump the version in [`debian/changelog`](debian/changelog) and `pkgver` in the `PKGBUILD`. 2. Tag the commit (`git tag -s vX.Y.Z`) and push it. 3. In `packaging/archlinux`, run `updpkgsums` to refresh the tarball checksum and `makepkg --printsrcinfo > .SRCINFO`. ## Development ```sh make build # build ./lazyntfy make test # go test ./... make vet # go vet ./... make fmt # gofmt -w . make run TOPIC=alerts make man # install just the man page ``` Run a single package's tests, for example: ```sh go test ./internal/history/... ``` ## Project layout | Path | Purpose | | --- | --- | | `cmd/lazyntfy` | flags and program startup | | `internal/config` | `client.yml` plus CLI overrides | | `internal/history` | on-disk topics and messages (SQLite) | | `internal/ntfy` | HTTP subscribe, publish, reconnect | | `internal/store` | newest-first messages, delete, clear | | `internal/alert` | Grafana / Alertmanager summary | | `internal/theme` | Catppuccin adaptive palette | | `internal/textutil` | width-aware text wrapping and clipping | | `internal/tui` | Bubble Tea interface | | `man` | `lazyntfy(1)` man page | | `debian` | Debian / Ubuntu package definition | | `packaging/archlinux` | Arch Linux `PKGBUILD` | ## Contributing 1. Fork the repository and create a topic branch. 2. Keep commits focused and conventional (`feat:`, `fix:`, `docs:`, …). 3. Run `make fmt vet test` before opening a pull request. ## License GNU General Public License, version 3 or later. See [LICENSE](LICENSE).