Summary
Two violations of the freedesktop Desktop Notifications Specification, both caused by the same function (calculateDuration in Services/System/NotificationService.qml), filed together.
1. expire_timeout=0 is ignored unless respectExpireTimeout is enabled
The spec is unambiguous on this (Notify method, section 9):
If -1, the notification's expiration time is dependent on the notification server's settings, and may vary for the type of notification. If 0, never expire.
— https://specifications.freedesktop.org/notification-spec/latest/protocol.html
Current behavior in NotificationService.qml:
function calculateDuration(data) {
const durations = [..., ..., ...];
if (Settings.data.notifications?.respectExpireTimeout) {
if (data.expireTimeout === 0) return -1;
if (data.expireTimeout > 0) return data.expireTimeout;
}
return durations[data.urgency];
}
respectExpireTimeout defaults to false, so a client calling notify-send -t 0 (an explicit, documented request to never expire) is silently overridden by the urgency-based fallback. 0 is a contract defined by the spec, not a hint — clients use it precisely when they need persistence.
This was already raised in #419 and closed as fixed, but the underlying behavior is unchanged in 4.7.6 — only the opt-in toggle was added.
Proposed fix: always honor expire_timeout == 0 (never expire), even when respectExpireTimeout is false. The toggle can keep controlling positive timeouts for users who want server-side overrides, but 0 must not be overridable.
2. urgency=critical is not sticky by default
Also a spec violation, this time of section 7 (Urgency Levels):
Critical notifications should not automatically expire, as they are things that the user will most likely want to know about. They should only be closed when the user dismisses them, for example, by clicking on the notification.
— https://specifications.freedesktop.org/notification-spec/latest/urgency-levels.html
Apps that rely on this (pacman post-transaction reboot warnings, system update tools, low-battery alerts, etc.) send -u critical expecting persistence. Noctalia auto-dismisses them after criticalUrgencyDuration seconds (default 15s), which can cause real reboot/upgrade warnings to be missed.
For reference, all major implementations follow the spec here: dunst, mako, swaync, GNOME Shell, KDE Plasma, notify-osd.
Proposed fix: treat urgency=critical as sticky by default — durations[2] returns -1 unless the user explicitly configures a finite duration. Equivalent alternative: default criticalUrgencyDuration: 0 with 0 meaning "never expire", clearly documented in the settings UI.
Concrete reproducer (point 1)
Default config (respectExpireTimeout: false):
notify-send -t 0 -u critical "Test" "Should stay until dismissed"
The notification disappears after criticalUrgencyDuration seconds (15s by default) instead of staying. Both points fail in this single command.
Environment
- noctalia-shell 4.7.6-1 (Arch)
- niri 26.04
- Affected file:
Services/System/NotificationService.qml, function calculateDuration (~ line 398)
Why one issue instead of two
Both points hit the same function and the same default-behavior philosophy ("server overrides client"). Fixing them together avoids two rounds of breaking-default discussion.
Summary
Two violations of the freedesktop Desktop Notifications Specification, both caused by the same function (
calculateDurationinServices/System/NotificationService.qml), filed together.1.
expire_timeout=0is ignored unlessrespectExpireTimeoutis enabledThe spec is unambiguous on this (Notify method, section 9):
Current behavior in
NotificationService.qml:respectExpireTimeoutdefaults tofalse, so a client callingnotify-send -t 0(an explicit, documented request to never expire) is silently overridden by the urgency-based fallback.0is a contract defined by the spec, not a hint — clients use it precisely when they need persistence.This was already raised in #419 and closed as fixed, but the underlying behavior is unchanged in 4.7.6 — only the opt-in toggle was added.
Proposed fix: always honor
expire_timeout == 0(never expire), even whenrespectExpireTimeoutisfalse. The toggle can keep controlling positive timeouts for users who want server-side overrides, but0must not be overridable.2.
urgency=criticalis not sticky by defaultAlso a spec violation, this time of section 7 (Urgency Levels):
Apps that rely on this (pacman post-transaction reboot warnings, system update tools, low-battery alerts, etc.) send
-u criticalexpecting persistence. Noctalia auto-dismisses them aftercriticalUrgencyDurationseconds (default 15s), which can cause real reboot/upgrade warnings to be missed.For reference, all major implementations follow the spec here: dunst, mako, swaync, GNOME Shell, KDE Plasma, notify-osd.
Proposed fix: treat
urgency=criticalas sticky by default —durations[2]returns-1unless the user explicitly configures a finite duration. Equivalent alternative: defaultcriticalUrgencyDuration: 0with0meaning "never expire", clearly documented in the settings UI.Concrete reproducer (point 1)
Default config (
respectExpireTimeout: false):The notification disappears after
criticalUrgencyDurationseconds (15s by default) instead of staying. Both points fail in this single command.Environment
Services/System/NotificationService.qml, functioncalculateDuration(~ line 398)Why one issue instead of two
Both points hit the same function and the same default-behavior philosophy ("server overrides client"). Fixing them together avoids two rounds of breaking-default discussion.