Skip to content

Global settings

The [notify] table is where the daemon-wide knobs live: which channels catch every failure, how long an outbound delivery keeps retrying, how much bell history to hang onto, how hard to coalesce. Every key is optional, and the defaults are tuned for a single-machine setup.

This isn’t the page for declaring channels or routing rules, though — that’s [[notifier]] and [[notification_route]].

[notify]
global_notifiers = ["inapp"] # default — bell on
# default_timeout = "30s" # unset — uses the built-in 5-min retry budget
history_keep = 1024 # default
history_keep_for = "90d" # default
coalesce_window = "1h" # default
occurrence_ring = 10 # default
coalesce_outbound = true # default
KeyDefaultWhat it does
global_notifiers["inapp"]Channels added to every per-task notify list (deduplicated), and catch-all for tasks without one. Set [] to silence the bell.
default_timeout(unset)Caps the total retry budget for one outbound delivery. Unset = 5 minutes.
history_keep1024Cap on bell rows kept in SQLite. Older rows are pruned in one pass.
history_keep_for90dMaximum age of bell rows. Accepts d and w units.
coalesce_window1hWindow during which repeats with the same kind for the same task update one row instead of writing a new one.
occurrence_ring10Recent timestamps kept on each coalesced row.
coalesce_outboundtrueCoalesce outbound deliveries too, not just bell rows. Set false to send one message per event.

Set both history_keep and history_keep_for and they both apply at once — whichever trims harder is the one that bites.

[notify]
global_notifiers = [] # silence the bell entirely
# global_notifiers = ["slack-ops"] # or: every failure goes to that channel

global_notifiers = [] switches off the bell in both the Web UI and the TUI footer, and it also stops "inapp" from being slipped into your per-task fields. With it empty, notify_on_failure = ["slack-ops"] goes to slack-ops and nowhere else.

Reach for the empty form when the daemon runs unattended — a build agent, a CI shard — and nobody’s ever looking at the Web UI. Reach for a non-empty list when you want every failure funneled to one channel without sprinkling per-task fields all over the file.

When an outbound delivery hits a transient failure, it retries with exponential backoff. default_timeout is the cap on how much wall-clock time that whole retry loop is allowed to burn.

Leave it unset and the budget is 5 minutes — a delivery can keep trying for that long. Set default_timeout = "30s" and you cut it to half a minute, so a hard outage shows up in the bell that much sooner.

The per-attempt HTTP timeout is a separate thing entirely. Set default_timeout shorter than a single attempt and you’ve effectively asked for one try and no retries.

Bell rows live in SQLite, kept separate from run rows. Two knobs prune them:

  • history_keep — keep at most N rows; older ones are pruned in one pass.
  • history_keep_for — delete rows older than this duration.

Both run at once, and the stricter one wins. A background sweeper takes care of it — there’s nothing for you to trigger.

RunWisp folds together repeat events of the same kind for the same task, so you don’t get paged twice for the same thing. A task failing every minute leaves you one bell row that ticks upward, not 60 separate ones.

  • coalesce_window — within this window, repeats update the same row: count climbs and the latest timestamp is recorded.
  • occurrence_ring — how many recent timestamps to keep on the row. The UI uses these to draw a sparkline.

Outbound channels coalesce on the same key by default too (coalesce_outbound) — the Coalescing section of the model page has the full timeline and the check-in/summary cadence.

There is currently no way to turn coalescing off — setting coalesce_window = "0s" falls back to the default of 1h. To minimise grouping, set a very short window like "1s".

A small server with one outbound channel that wants delivery failures to surface quickly:

[notify]
global_notifiers = ["inapp"] # keep the bell
default_timeout = "30s" # cap retry budget at 30s
history_keep = 2000
history_keep_for = "60d"
coalesce_window = "30m"
occurrence_ring = 20

A headless build agent that sends everything off-host:

[notify]
global_notifiers = [] # nobody reads the bell