1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
|
# notifyd
A freedesktop notification daemon for this desktop, replacing dunst.
The daemon owns `org.freedesktop.Notifications` and holds the state. It
publishes the live queue and the history ring as JSON under
`$XDG_RUNTIME_DIR/notifyd/` for a quickshell renderer to draw, and `notifyctl`
is the only thing that talks back over D-Bus.
go build ./...
The design and the exact file contract are in the quickshell repo, at
`docs/superpowers/specs/2026-09-15-notification-daemon-design.md`.
## Install and handover
Build and install the two binaries and the script, beside the statusctl CLI:
go build -o ~/bin/notifyd ./cmd/notifyd
go build -o ~/bin/notifyctl ./cmd/notifyctl
install -m 755 scripts/notify-snooze.sh ~/bin/notify-snooze.sh
dunst is not removed until this proves itself. To switch:
1. Stop dunst (`pkill -x dunst` or its service) so the bus name is free.
2. Add `hl.exec_cmd("notifyd")` to `~/.config/hypr/sections/autostart.lua`,
beside the quickshell lines.
3. Change `rofipass`'s one `dunstctl close-all` to `notifyctl close-all`.
4. Start `notifyd` and send a test notification.
The renderer that draws the balloons is a separate plan; until it ships the
queue is visible through `notifyctl list`.
## Configuration
`~/.config/notifyd/config.json` (`$XDG_CONFIG_HOME` is honoured). Every key
is optional and the defaults are the behaviour without the file:
{
"timeouts": { "low": 10, "normal": 10, "critical": 0 },
"liveCap": 20,
"historyCap": 20,
"soundTheme": "freedesktop"
}
Timeouts are seconds up to a day (86400), 0 is never, and apply only when a client sends
`expire_timeout -1`. Caps clamp to 1..100; a lowered cap applies at the next
notification. A broken file is logged and read as the defaults.
`soundTheme` is the freedesktop sound theme an app's `sound-name` hint is
looked up in (then its `Inherits`, then `freedesktop`). The daemon publishes
each notification's resolved `sound` (a path, or `""`) and `silent` (the
app's `suppress-sound`); a `sound-file` hint wins over `sound-name` when it
is an absolute path to an existing file. Playing is the renderer's job.
The daemon reads it at start and on `notifyctl reload`. The quickshell
settings page writes it; the same file carries the balloon shell's keys
(`balloons`, `dndPassCritical`, `muted`), which the daemon ignores.
## License
GPLv2 only. See [LICENSE](LICENSE).
## Development Approach
This project is developed using AI-assisted tools. Code is generated with the help of AI based on human-provided specifications, design decisions, and iterative feedback.
All contributions are reviewed, tested, and curated by the maintainer before being included in the codebase. AI is used as a productivity and exploration tool, while human oversight remains central to all decisions.
The goal is to combine the flexibility of AI-assisted development with standard open-source practices such as transparency, review, and accountability.
|