131 lines
6.2 KiB
Markdown
131 lines
6.2 KiB
Markdown
# Notifications Templates
|
|
|
|
> **Imposed rule:** Work of this type or tasks of this type on this component must also be applied everywhere else it should be applied.
|
|
|
|
Templates shape the payload rendered for each channel when a rule action fires. They are deterministic, locale-aware artefacts stored alongside rules so Notify.Worker replicas can render identical messages regardless of environment.
|
|
|
|
---
|
|
|
|
## 1. Template lifecycle
|
|
|
|
1. **Authoring.** Operators create templates via the API (`POST /templates`) or UI. Each template binds to a channel type (`Slack`, `Teams`, `Email`, `Webhook`, `Custom`) and a locale.
|
|
2. **Reference.** Rule actions opt in by referencing the template key (`actions[].template`). Channel defaults apply when no template is specified.
|
|
3. **Rendering.** During delivery, the worker resolves the template (locale fallbacks included), executes it using the safe Handlebars-style engine, and passes the rendered payload plus metadata to the connector.
|
|
4. **Audit.** Rendered payloads stored in the delivery ledger include the `templateId` so operators can trace which text was used.
|
|
|
|
---
|
|
|
|
## 2. Template schema reference
|
|
|
|
| Field | Type | Notes |
|
|
|-------|------|-------|
|
|
| `templateId` | string | Stable identifier (UUID/slug). |
|
|
| `tenantId` | string | Must match the tenant header in API calls. |
|
|
| `channelType` | [`NotifyChannelType`](../ARCHITECTURE_NOTIFY.md#5-channels--connectors-plug-ins) | Determines connector payload envelope. |
|
|
| `key` | string | Human-readable key referenced by rules (`tmpl-critical`). |
|
|
| `locale` | string | BCP-47 tag, stored lower-case (`en-us`, `bg-bg`). |
|
|
| `body` | string | Template body; rendered strictly without executing arbitrary code. |
|
|
| `renderMode` | enum | `Markdown`, `Html`, `AdaptiveCard`, `PlainText`, or `Json`. Guides connector sanitisation. |
|
|
| `format` | enum | `Slack`, `Teams`, `Email`, `Webhook`, or `Json`. Signals delivery payload structure. |
|
|
| `description` | string? | Optional operator note. |
|
|
| `metadata` | map<string,string> | Sorted map for automation (layout hints, fallback text). |
|
|
| `createdBy`/`createdAt` | string?, instant | Auto-populated. |
|
|
| `updatedBy`/`updatedAt` | string?, instant | Auto-populated. |
|
|
| `schemaVersion` | string | Auto-upgraded on persistence. |
|
|
|
|
Templates are normalised: string fields trimmed, locale lower-cased, metadata sorted to preserve determinism.
|
|
|
|
---
|
|
|
|
## 3. Variables, helpers, and context
|
|
|
|
Templates receive a structured context derived from the Notify event, rule match, and rendering metadata.
|
|
|
|
| Path | Description |
|
|
|------|-------------|
|
|
| `event.*` | Canonical event envelope (`kind`, `tenant`, `ts`, `actor`). |
|
|
| `event.scope.*` | Namespace, repository, digest, image, component identifiers, labels, attributes. |
|
|
| `payload.*` | Raw event payload (e.g., `payload.verdict`, `payload.delta.*`, `payload.links.*`). |
|
|
| `rule.*` | Rule descriptor (`ruleId`, `name`, `labels`, `metadata`). |
|
|
| `action.*` | Action descriptor (`actionId`, `channel`, `digest`, `throttle`, `metadata`). |
|
|
| `policy.*` | Policy metadata when supplied (`revisionId`, `name`). |
|
|
| `topFindings[]` | Top-N findings summarised for convenience (vulnerability ID, severity, reachability). |
|
|
| `digest.*` | When rendering digest flushes: `window`, `openedAt`, `itemCount`. |
|
|
|
|
Built-in helpers mirror the architecture dossier:
|
|
|
|
| Helper | Usage |
|
|
|--------|-------|
|
|
| `severity_icon severity` | Returns emoji/text badge representing severity. |
|
|
| `link text url` | Produces channel-safe hyperlink. |
|
|
| `pluralize count "finding"` | Adds plural suffix when `count != 1`. |
|
|
| `truncate text maxLength` | Cuts strings while preserving determinism. |
|
|
| `code text` | Formats inline code (Markdown/HTML aware). |
|
|
|
|
Connectors may expose additional helpers via partials, but must remain deterministic and side-effect free.
|
|
|
|
---
|
|
|
|
## 4. Sample templates
|
|
|
|
### 4.1 Slack (Markdown + block kit)
|
|
|
|
```hbs
|
|
{{#*inline "findingLine"}}
|
|
- {{severity_icon severity}} {{vulnId}} ({{severity}}) in `{{component}}`
|
|
{{/inline}}
|
|
|
|
*:rotating_light: {{payload.summary.total}} findings {{#if payload.delta.newCritical}}(new critical: {{payload.delta.newCritical}}){{/if}}*
|
|
|
|
{{#if topFindings.length}}
|
|
Top findings:
|
|
{{#each topFindings}}{{> findingLine}}{{/each}}
|
|
{{/if}}
|
|
|
|
{{link "Open report in Console" payload.links.ui}}
|
|
```
|
|
|
|
### 4.2 Email (HTML + text alternative)
|
|
|
|
```hbs
|
|
<h2>{{payload.verdict}} for {{event.scope.repo}}</h2>
|
|
<p>{{payload.summary.total}} findings ({{payload.summary.blocked}} blocked, {{payload.summary.warned}} warned)</p>
|
|
<table>
|
|
<thead><tr><th>Finding</th><th>Severity</th><th>Package</th></tr></thead>
|
|
<tbody>
|
|
{{#each topFindings}}
|
|
<tr>
|
|
<td>{{this.vulnId}}</td>
|
|
<td>{{this.severity}}</td>
|
|
<td>{{this.component}}</td>
|
|
</tr>
|
|
{{/each}}
|
|
</tbody>
|
|
</table>
|
|
<p>{{link "View full analysis" payload.links.ui}}</p>
|
|
```
|
|
|
|
When delivering via email, connectors automatically attach a plain-text alternative derived from the rendered content to preserve accessibility.
|
|
|
|
---
|
|
|
|
## 5. Preview and validation
|
|
|
|
- `POST /channels/{id}/test` accepts an optional `templateId` and sample payload to produce a rendered preview without dispatching the event. Results include channel type, target, title/summary, locale, body hash, and connector metadata.
|
|
- UI previews rely on the same API and highlight connector fallbacks (e.g., Teams adaptive card vs. text fallback).
|
|
- Offline Kit scenarios can call `/internal/notify/templates/normalize` to ensure bundled templates match the canonical schema before packaging.
|
|
|
|
---
|
|
|
|
## 6. Best practices
|
|
|
|
- Keep channel-specific limits in mind (Slack block/character quotas, Teams adaptive card size, email line length). Lean on digests to summarise long lists.
|
|
- Provide locale-specific versions for high-volume tenants; Notify selects the closest locale, falling back to `en-us`.
|
|
- Store connector-specific hints (`metadata.layout`, `metadata.emoji`) in template metadata rather than rules when they affect rendering.
|
|
- Version template bodies through metadata (e.g., `metadata.revision: "2025-10-28"`) so tenants can track changes over time.
|
|
- Run test previews whenever introducing new helpers to confirm body hashes remain stable across environments.
|
|
|
|
---
|
|
|
|
> **Imposed rule reminder:** Work of this type or tasks of this type on this component must also be applied everywhere else it should be applied.
|