Channels

Alert delivery destinations · log, webhook, Discord, Slack, exec.

A channel is a named alert destination. Rules reference channels by id. Each channel has a type and a small block of opts.

[channels.<id>]
type = "<kind>"
[channels.<id>.opts]
# kind-specific options

Channel kinds

KindCargo feature
logaction-log
execaction-exec
webhookaction-webhook
discordaction-discord
slackaction-slack

Drop channels you do not need with --no-default-features on nanook-cli and an explicit feature list. See Collectors → Slim builds for the pattern.

log

Writes the alert event through the agent's structured log stream. Zero config, good for development.

[channels.log]
type = "log"

webhook

POSTs a JSON payload to an HTTP endpoint. Default body is the alert event as JSON; pass a body template to override.

[channels.notify]
type = "webhook"
[channels.notify.opts]
url     = "https://hooks.example.com/nanook"
method  = "POST"        # default POST, accepts GET/POST/PUT/PATCH/DELETE
retries = 3
backoff = 1000          # milliseconds
body    = '{"text": "{{ rule }} fired with {{ trigger.val }}"}'

discord

Webhook-shaped, with Discord-specific defaults (embeds, escaping). Takes the Discord webhook URL.

[channels.discord]
type = "discord"
[channels.discord.opts]
url  = "${DISCORD_WEBHOOK_URL}"
body = "**{{ rule }}** at {{ trigger.val | round(1) }} on {{ trigger.source }}"

slack

Slack-flavoured. Uses a Slack Incoming Webhook URL.

[channels.ops]
type = "slack"
[channels.ops.opts]
url  = "${SLACK_WEBHOOK_URL}"
body = ":fire: *{{ rule }}* fired with value `{{ trigger.val | round(1) }}`"

exec

Run a local command per alert. The cmd option is a nanook-template rendered before exec, so context splices straight into the command line. The agent also exports NANOOK_ALERT_* env vars for the spawned process.

[channels.shell]
type = "exec"
[channels.shell.opts]
cmd = "/usr/local/bin/page-oncall.sh --service {{ trigger.source }} --severity high"

Body templating

The body field on webhook / Discord / Slack channels is a nanook-template rendered against the AlertEvent. Common paths:

  • kind (fire, resolve, escalate)
  • rule (rule expression as a string)
  • channel (destination channel id)
  • at (RFC3339 timestamp)
  • trigger.name (metric name)
  • trigger.val (value that tripped the rule)
  • trigger.source (collector that emitted it)
  • trigger.labels.<key> (label value)

log channels ignore body. exec channels render cmd instead, with the same context.

Routing alerts

Each [[alerts]] rule names exactly one channel. Route different rules to different channels:

[[alerts]]
expr    = "cpu.usage > 90%"
channel = "log"

[[alerts]]
expr    = "disk.usage > 95%"
channel = "ops"

For escalation, see Alerts.

Testing

There's no "fire one rule on demand" command yet. While iterating:

nanook ctl trigger <collector>   # fire one collector read; rules evaluate as normal
nanook probe <collector>         # one-shot read, prints metrics, no alerting
nanook check                     # parse the config and build every channel handler

Run nanook check after editing channel opts to confirm template syntax before reload.