Align AOC tasks for Excititor and Concelier
This commit is contained in:
@@ -1,92 +1,92 @@
|
||||
# Notifications Digests
|
||||
|
||||
> **Imposed rule:** Work of this type or tasks of this type on this component must also be applied everywhere else it should be applied.
|
||||
|
||||
Digests coalesce multiple matching events into a single notification when rules request batched delivery. They protect responders from alert storms while preserving a deterministic record of every input.
|
||||
|
||||
---
|
||||
|
||||
## 1. Digest lifecycle
|
||||
|
||||
1. **Window selection.** Rule actions opt into a digest cadence by setting `actions[].digest` (`instant`, `5m`, `15m`, `1h`, `1d`). `instant` skips digest logic entirely.
|
||||
2. **Aggregation.** When an event matches, the worker appends it to the open digest window (`tenantId + actionId + window`). Events include the canonical scope, delta counts, and references.
|
||||
3. **Flush.** When the window expires or hits the worker’s safety cap (configurable), the worker renders a digest template and emits a single delivery with status `Digested`.
|
||||
4. **Audit.** The delivery ledger links back to the digest document so operators can inspect individual items and the aggregated summary.
|
||||
|
||||
---
|
||||
|
||||
## 2. Storage model
|
||||
|
||||
Digest state lives in Mongo (`digests` collection) and mirrors the schema described in [modules/notify/architecture.md](../modules/notify/architecture.md#7-data-model-mongo):
|
||||
|
||||
```json
|
||||
{
|
||||
"_id": "tenant-dev:act-email-compliance:1h",
|
||||
"tenantId": "tenant-dev",
|
||||
"actionKey": "act-email-compliance",
|
||||
"window": "1h",
|
||||
"openedAt": "2025-10-24T08:00:00Z",
|
||||
"status": "open",
|
||||
"items": [
|
||||
{
|
||||
"eventId": "00000000-0000-0000-0000-000000000001",
|
||||
"scope": {
|
||||
"namespace": "prod-payments",
|
||||
"repo": "ghcr.io/acme/api",
|
||||
"digest": "sha256:…"
|
||||
},
|
||||
"delta": {
|
||||
"newCritical": 1,
|
||||
"kev": 1
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
- `status` reflects whether the window is currently collecting (`open`) or has been completed (`closed`). Future revisions may introduce `flushing` for in-progress operations.
|
||||
- `items[].delta` captures aggregated counts for reporting (e.g., new critical findings, KEV, quieted).
|
||||
- Workers use optimistic concurrency on the document ID to avoid duplicate flushes across replicas.
|
||||
|
||||
---
|
||||
|
||||
## 3. Rendering and templates
|
||||
|
||||
- Digest deliveries use the same template engine as instant notifications. Templates receive an additional `digest` object with `window`, `openedAt`, `itemCount`, and `items` (findings grouped by namespace/repository when available).
|
||||
- Provide digest-specific templates (e.g., `tmpl-digest-hourly`) so the body can enumerate top offenders, summarise totals, and link to detailed dashboards.
|
||||
- When no template is specified, Notify falls back to channel defaults that emphasise summary counts and redirect to Console for detail.
|
||||
|
||||
---
|
||||
|
||||
## 4. API surface
|
||||
|
||||
| Endpoint | Description | Notes |
|
||||
|----------|-------------|-------|
|
||||
| `POST /digests` | Issues administrative commands (e.g., force flush, reopen) for a specific action/window. | Request body specifies the command target; requires `notify.admin`. |
|
||||
| `GET /digests/{actionKey}` | Returns the currently open window (if any) for the referenced action. | Supports operators/CLI inspecting pending digests; requires `notify.read`. |
|
||||
| `DELETE /digests/{actionKey}` | Drops the open window without notifying (emergency stop). | Emits an audit record; use sparingly. |
|
||||
|
||||
All routes honour the tenant header and reuse the standard Notify rate limits.
|
||||
|
||||
---
|
||||
|
||||
## 5. Worker behaviour and safety nets
|
||||
|
||||
- **Idempotency.** Flush operations generate a deterministic digest delivery ID (`digest:<tenant>:<actionId>:<window>:<openedAt>`). Retries reuse the same ID.
|
||||
- **Throttles.** Digest generation respects action throttles; setting an aggressive throttle together with a digest window may result in deliberate skips (logged as `Throttled` in the delivery ledger).
|
||||
- **Quiet hours.** Future sprint work (`NOTIFY-SVC-39-004`) integrates quiet-hour calendars. When enabled, flush timers pause during quiet windows and resume afterwards.
|
||||
- **Back-pressure.** When the window reaches the configured item cap before the timer, the worker flushes early and starts a new window immediately.
|
||||
- **Crash resilience.** Workers rebuild in-flight windows from Mongo on startup; partially flushed windows remain closed after success or reopened if the flush fails.
|
||||
|
||||
---
|
||||
|
||||
## 6. Operator guidance
|
||||
|
||||
- Choose hourly digests for high-volume compliance events; daily digests suit executive reporting.
|
||||
- Pair digests with incident-focused instant rules so critical items surface immediately while less urgent noise is summarised.
|
||||
- Monitor `/stats` output for `openDigestCount` to ensure windows are flushing; spikes may indicate downstream connector failures.
|
||||
- When testing new digest templates, open a small (`5m`) window, trigger sample events, then call `POST /digests/{actionId}/flush` to validate rendering before moving to longer cadences.
|
||||
|
||||
---
|
||||
|
||||
> **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.
|
||||
# Notifications Digests
|
||||
|
||||
> **Imposed rule:** Work of this type or tasks of this type on this component must also be applied everywhere else it should be applied.
|
||||
|
||||
Digests coalesce multiple matching events into a single notification when rules request batched delivery. They protect responders from alert storms while preserving a deterministic record of every input.
|
||||
|
||||
---
|
||||
|
||||
## 1. Digest lifecycle
|
||||
|
||||
1. **Window selection.** Rule actions opt into a digest cadence by setting `actions[].digest` (`instant`, `5m`, `15m`, `1h`, `1d`). `instant` skips digest logic entirely.
|
||||
2. **Aggregation.** When an event matches, the worker appends it to the open digest window (`tenantId + actionId + window`). Events include the canonical scope, delta counts, and references.
|
||||
3. **Flush.** When the window expires or hits the worker’s safety cap (configurable), the worker renders a digest template and emits a single delivery with status `Digested`.
|
||||
4. **Audit.** The delivery ledger links back to the digest document so operators can inspect individual items and the aggregated summary.
|
||||
|
||||
---
|
||||
|
||||
## 2. Storage model
|
||||
|
||||
Digest state lives in Mongo (`digests` collection) and mirrors the schema described in [modules/notify/architecture.md](../modules/notify/architecture.md#7-data-model-mongo):
|
||||
|
||||
```json
|
||||
{
|
||||
"_id": "tenant-dev:act-email-compliance:1h",
|
||||
"tenantId": "tenant-dev",
|
||||
"actionKey": "act-email-compliance",
|
||||
"window": "1h",
|
||||
"openedAt": "2025-10-24T08:00:00Z",
|
||||
"status": "open",
|
||||
"items": [
|
||||
{
|
||||
"eventId": "00000000-0000-0000-0000-000000000001",
|
||||
"scope": {
|
||||
"namespace": "prod-payments",
|
||||
"repo": "ghcr.io/acme/api",
|
||||
"digest": "sha256:…"
|
||||
},
|
||||
"delta": {
|
||||
"newCritical": 1,
|
||||
"kev": 1
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
- `status` reflects whether the window is currently collecting (`open`) or has been completed (`closed`). Future revisions may introduce `flushing` for in-progress operations.
|
||||
- `items[].delta` captures aggregated counts for reporting (e.g., new critical findings, KEV, quieted).
|
||||
- Workers use optimistic concurrency on the document ID to avoid duplicate flushes across replicas.
|
||||
|
||||
---
|
||||
|
||||
## 3. Rendering and templates
|
||||
|
||||
- Digest deliveries use the same template engine as instant notifications. Templates receive an additional `digest` object with `window`, `openedAt`, `itemCount`, and `items` (findings grouped by namespace/repository when available).
|
||||
- Provide digest-specific templates (e.g., `tmpl-digest-hourly`) so the body can enumerate top offenders, summarise totals, and link to detailed dashboards.
|
||||
- When no template is specified, Notify falls back to channel defaults that emphasise summary counts and redirect to Console for detail.
|
||||
|
||||
---
|
||||
|
||||
## 4. API surface
|
||||
|
||||
| Endpoint | Description | Notes |
|
||||
|----------|-------------|-------|
|
||||
| `POST /digests` | Issues administrative commands (e.g., force flush, reopen) for a specific action/window. | Request body specifies the command target; requires `notify.admin`. |
|
||||
| `GET /digests/{actionKey}` | Returns the currently open window (if any) for the referenced action. | Supports operators/CLI inspecting pending digests; requires `notify.read`. |
|
||||
| `DELETE /digests/{actionKey}` | Drops the open window without notifying (emergency stop). | Emits an audit record; use sparingly. |
|
||||
|
||||
All routes honour the tenant header and reuse the standard Notify rate limits.
|
||||
|
||||
---
|
||||
|
||||
## 5. Worker behaviour and safety nets
|
||||
|
||||
- **Idempotency.** Flush operations generate a deterministic digest delivery ID (`digest:<tenant>:<actionId>:<window>:<openedAt>`). Retries reuse the same ID.
|
||||
- **Throttles.** Digest generation respects action throttles; setting an aggressive throttle together with a digest window may result in deliberate skips (logged as `Throttled` in the delivery ledger).
|
||||
- **Quiet hours.** Future sprint work (`NOTIFY-SVC-39-004`) integrates quiet-hour calendars. When enabled, flush timers pause during quiet windows and resume afterwards.
|
||||
- **Back-pressure.** When the window reaches the configured item cap before the timer, the worker flushes early and starts a new window immediately.
|
||||
- **Crash resilience.** Workers rebuild in-flight windows from Mongo on startup; partially flushed windows remain closed after success or reopened if the flush fails.
|
||||
|
||||
---
|
||||
|
||||
## 6. Operator guidance
|
||||
|
||||
- Choose hourly digests for high-volume compliance events; daily digests suit executive reporting.
|
||||
- Pair digests with incident-focused instant rules so critical items surface immediately while less urgent noise is summarised.
|
||||
- Monitor `/stats` output for `openDigestCount` to ensure windows are flushing; spikes may indicate downstream connector failures.
|
||||
- When testing new digest templates, open a small (`5m`) window, trigger sample events, then call `POST /digests/{actionId}/flush` to validate rendering before moving to longer cadences.
|
||||
|
||||
---
|
||||
|
||||
> **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.
|
||||
|
||||
@@ -1,76 +1,76 @@
|
||||
# Notifications Overview
|
||||
|
||||
> **Imposed rule:** Work of this type or tasks of this type on this component must also be applied everywhere else it should be applied.
|
||||
|
||||
Notifications Studio turns raw platform events into concise, tenant-scoped alerts that reach the right responders without overwhelming them. The service is sovereign/offline-first, follows the Aggregation-Only Contract (AOC), and produces deterministic outputs so the same configuration yields identical deliveries across environments.
|
||||
|
||||
---
|
||||
|
||||
## 1. Mission & value
|
||||
|
||||
- **Reduce noise.** Only materially new or high-impact changes reach chat, email, or webhooks thanks to rule filters, throttles, and digest windows.
|
||||
- **Explainable results.** Every delivery is traceable back to a rule, action, and event payload stored in the delivery ledger; operators can audit what fired and why.
|
||||
- **Safe by default.** Secrets remain in external stores, templates are sandboxed, quiet hours and throttles prevent storms, and idempotency guarantees protect downstream systems.
|
||||
- **Offline-aligned.** All configuration, templates, and plug-ins ship with Offline Kits; no external SaaS is required to send notifications.
|
||||
|
||||
---
|
||||
|
||||
## 2. Core capabilities
|
||||
|
||||
| Capability | What it does | Key docs |
|
||||
|------------|--------------|----------|
|
||||
| Rules engine | Declarative matchers for event kinds, severities, namespaces, VEX context, KEV flags, and more. | [`notifications/rules.md`](rules.md) |
|
||||
| Channel catalog | Slack, Teams, Email, Webhook connectors loaded via restart-time plug-ins; metadata stored without secrets. | [`notifications/architecture.md`](architecture.md) |
|
||||
| Templates | Locale-aware, deterministic rendering via safe helpers; channel defaults plus tenant-specific overrides. | [`notifications/templates.md`](templates.md) |
|
||||
| Digests | Coalesce bursts into periodic summaries with deterministic IDs and audit trails. | [`notifications/digests.md`](digests.md) |
|
||||
| Delivery ledger | Tracks rendered payload hashes, attempts, throttles, and outcomes for every action. | [`modules/notify/architecture.md`](../modules/notify/architecture.md#7-data-model-mongo) |
|
||||
|
||||
---
|
||||
|
||||
## 3. How it fits into Stella Ops
|
||||
|
||||
1. **Producers emit events.** Scanner, Scheduler, VEX Lens, Attestor, and Zastava publish canonical envelopes (`NotifyEvent`) onto the internal bus.
|
||||
2. **Notify.Worker evaluates rules.** For each tenant, the worker applies match filters, VEX gates, throttles, and digest policies before rendering the action.
|
||||
3. **Connectors deliver.** Channel plug-ins send the rendered payload to Slack/Teams/Email/Webhook targets and report back attempts and outcomes.
|
||||
4. **Consumers investigate.** Operators pivot from message links into Console dashboards, SBOM views, or policy overlays with correlation IDs preserved.
|
||||
|
||||
The Notify WebService fronts worker state with REST APIs used by the UI and CLI. Tenants authenticate via StellaOps Authority scopes `notify.read` and `notify.admin`. All operations require the tenant header (`X-StellaOps-Tenant`) to preserve sovereignty boundaries.
|
||||
|
||||
---
|
||||
|
||||
## 4. Operating model
|
||||
|
||||
| Area | Guidance |
|
||||
|------|----------|
|
||||
| **Tenancy** | Each rule, channel, template, and delivery belongs to exactly one tenant. Cross-tenant sharing is intentionally unsupported. |
|
||||
| **Determinism** | Configuration persistence normalises strings and sorts collections. Template rendering produces identical `bodyHash` values when inputs match. |
|
||||
| **Scaling** | Workers scale horizontally; per-tenant rule snapshots are cached and refreshed from Mongo change streams. Redis (or equivalent) guards throttles and locks. |
|
||||
| **Offline** | Offline Kits include plug-ins, default templates, and seed rules. Operators can edit YAML/JSON manifests before air-gapped deployment. |
|
||||
| **Security** | Channel secrets use indirection (`secretRef`), Authority-protected OAuth clients secure API access, and delivery payloads are redacted before storage where required. |
|
||||
|
||||
---
|
||||
|
||||
## 5. Getting started (first 30 minutes)
|
||||
|
||||
| Step | Goal | Reference |
|
||||
|------|------|-----------|
|
||||
| 1 | Deploy Notify WebService + Worker with Mongo and Redis | [`modules/notify/architecture.md`](../modules/notify/architecture.md#1-runtime-shape--projects) |
|
||||
# Notifications Overview
|
||||
|
||||
> **Imposed rule:** Work of this type or tasks of this type on this component must also be applied everywhere else it should be applied.
|
||||
|
||||
Notifications Studio turns raw platform events into concise, tenant-scoped alerts that reach the right responders without overwhelming them. The service is sovereign/offline-first, follows the Aggregation-Only Contract (AOC), and produces deterministic outputs so the same configuration yields identical deliveries across environments.
|
||||
|
||||
---
|
||||
|
||||
## 1. Mission & value
|
||||
|
||||
- **Reduce noise.** Only materially new or high-impact changes reach chat, email, or webhooks thanks to rule filters, throttles, and digest windows.
|
||||
- **Explainable results.** Every delivery is traceable back to a rule, action, and event payload stored in the delivery ledger; operators can audit what fired and why.
|
||||
- **Safe by default.** Secrets remain in external stores, templates are sandboxed, quiet hours and throttles prevent storms, and idempotency guarantees protect downstream systems.
|
||||
- **Offline-aligned.** All configuration, templates, and plug-ins ship with Offline Kits; no external SaaS is required to send notifications.
|
||||
|
||||
---
|
||||
|
||||
## 2. Core capabilities
|
||||
|
||||
| Capability | What it does | Key docs |
|
||||
|------------|--------------|----------|
|
||||
| Rules engine | Declarative matchers for event kinds, severities, namespaces, VEX context, KEV flags, and more. | [`notifications/rules.md`](rules.md) |
|
||||
| Channel catalog | Slack, Teams, Email, Webhook connectors loaded via restart-time plug-ins; metadata stored without secrets. | [`notifications/architecture.md`](architecture.md) |
|
||||
| Templates | Locale-aware, deterministic rendering via safe helpers; channel defaults plus tenant-specific overrides. | [`notifications/templates.md`](templates.md) |
|
||||
| Digests | Coalesce bursts into periodic summaries with deterministic IDs and audit trails. | [`notifications/digests.md`](digests.md) |
|
||||
| Delivery ledger | Tracks rendered payload hashes, attempts, throttles, and outcomes for every action. | [`modules/notify/architecture.md`](../modules/notify/architecture.md#7-data-model-mongo) |
|
||||
|
||||
---
|
||||
|
||||
## 3. How it fits into Stella Ops
|
||||
|
||||
1. **Producers emit events.** Scanner, Scheduler, VEX Lens, Attestor, and Zastava publish canonical envelopes (`NotifyEvent`) onto the internal bus.
|
||||
2. **Notify.Worker evaluates rules.** For each tenant, the worker applies match filters, VEX gates, throttles, and digest policies before rendering the action.
|
||||
3. **Connectors deliver.** Channel plug-ins send the rendered payload to Slack/Teams/Email/Webhook targets and report back attempts and outcomes.
|
||||
4. **Consumers investigate.** Operators pivot from message links into Console dashboards, SBOM views, or policy overlays with correlation IDs preserved.
|
||||
|
||||
The Notify WebService fronts worker state with REST APIs used by the UI and CLI. Tenants authenticate via StellaOps Authority scopes `notify.read` and `notify.admin`. All operations require the tenant header (`X-StellaOps-Tenant`) to preserve sovereignty boundaries.
|
||||
|
||||
---
|
||||
|
||||
## 4. Operating model
|
||||
|
||||
| Area | Guidance |
|
||||
|------|----------|
|
||||
| **Tenancy** | Each rule, channel, template, and delivery belongs to exactly one tenant. Cross-tenant sharing is intentionally unsupported. |
|
||||
| **Determinism** | Configuration persistence normalises strings and sorts collections. Template rendering produces identical `bodyHash` values when inputs match. |
|
||||
| **Scaling** | Workers scale horizontally; per-tenant rule snapshots are cached and refreshed from Mongo change streams. Redis (or equivalent) guards throttles and locks. |
|
||||
| **Offline** | Offline Kits include plug-ins, default templates, and seed rules. Operators can edit YAML/JSON manifests before air-gapped deployment. |
|
||||
| **Security** | Channel secrets use indirection (`secretRef`), Authority-protected OAuth clients secure API access, and delivery payloads are redacted before storage where required. |
|
||||
|
||||
---
|
||||
|
||||
## 5. Getting started (first 30 minutes)
|
||||
|
||||
| Step | Goal | Reference |
|
||||
|------|------|-----------|
|
||||
| 1 | Deploy Notify WebService + Worker with Mongo and Redis | [`modules/notify/architecture.md`](../modules/notify/architecture.md#1-runtime-shape--projects) |
|
||||
| 2 | Register OAuth clients/scopes in Authority | [`etc/authority.yaml.sample`](../../etc/authority.yaml.sample) |
|
||||
| 3 | Install channel plug-ins and capture secret references | [`plugins/notify`](../../plugins) |
|
||||
| 4 | Create a tenant rule and test preview | [`POST /channels/{id}/test`](../modules/notify/architecture.md#8-external-apis-webservice) |
|
||||
| 5 | Inspect deliveries and digests | `/api/v1/notify/deliveries`, `/api/v1/notify/digests` |
|
||||
|
||||
---
|
||||
|
||||
## 6. Alignment with implementation work
|
||||
|
||||
| Backlog item | Impact on docs | Status |
|
||||
|--------------|----------------|--------|
|
||||
| `NOTIFY-SVC-38-001..004` | Foundational correlation, throttling, simulation hooks. | **In progress** – align behaviour once services publish beta APIs. |
|
||||
| `NOTIFY-SVC-39-001..004` | Adds correlation engine, digest generator, simulation API, quiet hours. | **Pending** – revisit rule/digest sections when these tasks merge. |
|
||||
|
||||
Action: coordinate with the Notifications Service Guild when `NOTIFY-SVC-39-001..004` land to validate payload fields, quiet-hours semantics, and any new connector metadata that should be documented here and in the channel-specific guides.
|
||||
|
||||
---
|
||||
|
||||
> **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.
|
||||
| 3 | Install channel plug-ins and capture secret references | [`plugins/notify`](../../plugins) |
|
||||
| 4 | Create a tenant rule and test preview | [`POST /channels/{id}/test`](../modules/notify/architecture.md#8-external-apis-webservice) |
|
||||
| 5 | Inspect deliveries and digests | `/api/v1/notify/deliveries`, `/api/v1/notify/digests` |
|
||||
|
||||
---
|
||||
|
||||
## 6. Alignment with implementation work
|
||||
|
||||
| Backlog item | Impact on docs | Status |
|
||||
|--------------|----------------|--------|
|
||||
| `NOTIFY-SVC-38-001..004` | Foundational correlation, throttling, simulation hooks. | **In progress** – align behaviour once services publish beta APIs. |
|
||||
| `NOTIFY-SVC-39-001..004` | Adds correlation engine, digest generator, simulation API, quiet hours. | **Pending** – revisit rule/digest sections when these tasks merge. |
|
||||
|
||||
Action: coordinate with the Notifications Service Guild when `NOTIFY-SVC-39-001..004` land to validate payload fields, quiet-hours semantics, and any new connector metadata that should be documented here and in the channel-specific guides.
|
||||
|
||||
---
|
||||
|
||||
> **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.
|
||||
|
||||
@@ -1,130 +1,130 @@
|
||||
# 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`](../modules/notify/architecture.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.
|
||||
# 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`](../modules/notify/architecture.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.
|
||||
|
||||
Reference in New Issue
Block a user