nanook-templates
Render alert bodies, webhook payloads, exec args.
nanook-templates is the small templating language used wherever nanook splices runtime data into a string: alert bodies, webhook URLs, exec command lines. Jinja-ish syntax, parses at config load so typos surface during nanook check.
The four sites
A template string is a mix of:
- Literals · anything outside
{{ }}and{% %}. - Substitutions ·
{{ expr }}evaluates and inserts the result. - Conditionals ·
{% if expr %}then{% else %}otherwise{% endif %}. - Loops ·
{% for x in iter %}body{% endfor %}.
To emit a literal {{, escape it as \{{.
Quick taste
fired with on
core pinned hot
Substitutions
{{ expr }}. Whitespace inside is trimmed.
Paths
Dotted field access and bracket key lookup. The render context for an alert template is the full AlertEvent:
| Path | What |
|---|---|
kind | fire, resolve, or escalate |
rule | The rule's expression as a string |
channel | The destination channel id |
message | The default rendered alert message |
at | RFC3339 timestamp of the dispatch |
trigger.name | The metric name (e.g. cpu.usage) |
trigger.val | The value that tripped the rule |
trigger.source | The collector that emitted it |
trigger.labels.<key> | Label value by name (e.g. trigger.labels.core) |
trigger.timestamp | RFC3339 timestamp of the sample |
trigger.id | Stable id for the metric series |
trigger is absent on window-recheck rules where no specific sample drove the transition. Guard with or if you reference it from a rule that may fire that way.
# 92.4
# /var
# bracket form, same thing
# cpu
Literals
or fallback
{{ left or right }} evaluates left. If it errors or comes back empty, it evaluates right instead. Chain left-to-right.
Filters
Pipe a value through a transform. Filters chain.
Built-in filters:
| Filter | Args | What |
|---|---|---|
upper | – | uppercase |
lower | – | lowercase |
trim | – | strip leading/trailing whitespace |
len | – | length of string, list, or map |
default(x) | 1 | replace empty input with x |
truncate(n) | 1 | clip to N chars, append … if clipped |
round(n) | 1 | round float to N decimals |
floor | – | floor a number |
ceil | – | ceil a number |
urlencode | – | percent-encode for URL query strings |
sh | – | POSIX single-quote escape for shell command lines |
json | – | JSON-encode the value (strings get quoted+escaped) |
sh and json exist because templating values into shell command lines or JSON bodies is otherwise a quoting hazard. Reach for them whenever a value crosses into one of those layers:
cmd = "mail -s alice@example.com"
body = '{"text": , "host": }'
sh always wraps in single quotes (so the shell sees one argument no matter what's inside). json produces valid JSON for any value: strings come out quoted and escaped, numbers and bools pass through, lists become arrays, maps become objects, missing fields become null.
Conditionals
PAGE THE ONCALL
log it and move on
Truthiness is loose on purpose. Falsy values: "", "false", "0", plus any expression that errors during render. Everything else is truthy.
{% else %} is optional. There is no {% elif %}. Nest {% if %} blocks if you need branching.
Loops
mounts:
- : %
Iterates over a list, set, or map's values. The bound name (m here) shadows any outer field of the same name inside the loop body.
Fall-through behaviour
A path that doesn't resolve raises a render error. The owning handler decides what to do: webhook channels fall back to the default JSON body, exec channels fall back to the raw template source, log channels fall back to the default message. Use or and default to make templates robust.
# robust
# fragile, errors if trigger.labels.region is missing
Where templates appear
| Field | Template? |
|---|---|
[channels.<id>.opts].url (webhook, discord, slack) | yes (you can interpolate secrets here) |
[channels.<id>.opts].body (webhook, discord, slack) | yes |
[channels.<id>.opts].cmd (exec channel) | yes |
[[alerts]].body | yes, rendered by every action with a fallback to the default message on render error |
When in doubt, run nanook check.
Escaping
\{{ this is a literal pair of braces }}
There is no escape for {% because it's vanishingly rare in plain prose. If you really need a literal {%, split it across the literal: { %.