From 453a9b7e2a0cd3ef80a4254031a304f7aec67fa4 Mon Sep 17 00:00:00 2001 From: Christian Krakau-Louis Date: Sat, 23 May 2026 18:41:10 +0200 Subject: [PATCH] feat: add ticketing chatops templates --- .../app/api/api_v1/endpoints/integrations.py | 7 + .../services/ticketing_chatops_templates.py | 313 ++++++++++++++++++ .../tests/test_ticketing_chatops_templates.py | 101 ++++++ docs/milestones.md | 4 +- docs/reference/api.md | 7 + .../ticketing-chatops-integrations.md | 176 ++++++++++ docs/user_guide/settings.md | 5 + mkdocs.yml | 1 + 8 files changed, 612 insertions(+), 2 deletions(-) create mode 100644 backend/app/services/ticketing_chatops_templates.py create mode 100644 backend/app/tests/test_ticketing_chatops_templates.py create mode 100644 docs/reference/ticketing-chatops-integrations.md diff --git a/backend/app/api/api_v1/endpoints/integrations.py b/backend/app/api/api_v1/endpoints/integrations.py index f659589..3f00b29 100644 --- a/backend/app/api/api_v1/endpoints/integrations.py +++ b/backend/app/api/api_v1/endpoints/integrations.py @@ -4,6 +4,7 @@ from fastapi import APIRouter, Depends from app.core.security import require_admin_auth from app.services.siem_templates import get_siem_templates +from app.services.ticketing_chatops_templates import get_ticketing_chatops_templates router = APIRouter() @@ -12,3 +13,9 @@ router = APIRouter() async def siem_templates(_auth: dict = Depends(require_admin_auth)): """Return versioned schemas and examples for SIEM ingestion.""" return get_siem_templates() + + +@router.get("/ticketing-chatops/templates") +async def ticketing_chatops_templates(_auth: dict = Depends(require_admin_auth)): + """Return ticketing and chatops workflow templates.""" + return get_ticketing_chatops_templates() diff --git a/backend/app/services/ticketing_chatops_templates.py b/backend/app/services/ticketing_chatops_templates.py new file mode 100644 index 0000000..4e467f5 --- /dev/null +++ b/backend/app/services/ticketing_chatops_templates.py @@ -0,0 +1,313 @@ +"""Ticketing and chatops integration templates.""" + +from __future__ import annotations + +import copy +from typing import Any, Dict, List + +from app.services.webhook_events import ( + EVENT_ALERT_CREATED, + EVENT_ALERT_RESOLVED, + EVENT_COMPLIANCE_DROP, + EVENT_REPORTS_MISSING, + EVENT_SENDER_NEW, +) + +TICKETING_CHATOPS_TEMPLATE_VERSION = "2026-05-23" +TICKETING_CHATOPS_SCHEMA_VERSION = "dmarq.workflow.template.v1" + +WORKFLOW_EVENT_TYPES = [ + EVENT_SENDER_NEW, + EVENT_COMPLIANCE_DROP, + EVENT_REPORTS_MISSING, + EVENT_ALERT_CREATED, + EVENT_ALERT_RESOLVED, +] + +EVENT_WORKFLOW_MAPPINGS: Dict[str, Dict[str, Any]] = { + EVENT_SENDER_NEW: { + "severity": "medium", + "owner": "email-security", + "ticket_action": "create_or_update", + "chat_action": "notify_channel", + "dedupe_key_template": "dmarq:{event_type}:{domain}:{sender_ip}", + "summary_template": "Review new DMARC sending source for {domain}", + "noise_control": "Open one ticket per domain and sender IP. Suppress repeats for 7 days after acknowledgement.", + }, + EVENT_COMPLIANCE_DROP: { + "severity": "high", + "owner": "email-security", + "ticket_action": "create_or_update", + "chat_action": "notify_channel_and_thread", + "dedupe_key_template": "dmarq:{event_type}:{domain}", + "summary_template": "Investigate DMARC compliance drop for {domain}", + "noise_control": "Escalate only when drop_points meets the configured alert threshold. Update the same ticket while active.", + }, + EVENT_REPORTS_MISSING: { + "severity": "medium", + "owner": "mail-operations", + "ticket_action": "create_or_update", + "chat_action": "notify_channel", + "dedupe_key_template": "dmarq:{event_type}:{domain}", + "summary_template": "Restore missing DMARC aggregate reports for {domain}", + "noise_control": "Create one ticket per domain and keep reminders to one chat update per day.", + }, + EVENT_ALERT_CREATED: { + "severity": "medium", + "owner": "email-security", + "ticket_action": "create_or_update", + "chat_action": "notify_channel", + "dedupe_key_template": "dmarq:{event_type}:{domain}:{alert_rule}", + "summary_template": "DMARQ alert: {title}", + "noise_control": "Route by alert_rule and reuse open tickets while the alert remains active.", + }, + EVENT_ALERT_RESOLVED: { + "severity": "info", + "owner": "email-security", + "ticket_action": "resolve_or_comment", + "chat_action": "notify_thread", + "dedupe_key_template": "dmarq:{event_type}:{domain}:{alert_rule}", + "summary_template": "DMARQ alert resolved: {title}", + "noise_control": "Resolve the linked ticket when all active signals for the dedupe key are clear.", + }, +} + +SAMPLE_CONTEXT: Dict[str, Any] = { + "event_type": EVENT_COMPLIANCE_DROP, + "event_id": "dmarq-compliance-drop-20260523-example-com", + "domain": "example.com", + "severity": "high", + "title": "DMARC compliance dropped for example.com", + "detail": "Compliance fell by 23.39 percentage points in the current window.", + "alert_rule": "compliance_drop", + "sender_ip": None, + "message_count": 1280, + "failed_count": 366, + "compliance_rate": 71.41, + "previous_compliance_rate": 94.8, + "drop_points": 23.39, + "domain_url": "https://dmarq.example/domains/example.com", + "report_url": "https://dmarq.example/domains/example.com/reports", + "dedupe_key": "dmarq:dmarq.compliance.drop:example.com", +} + +JIRA_ISSUE_TEMPLATE: Dict[str, Any] = { + "operation": "create_or_update_issue", + "lookup": { + "jql": 'project = EMAILSEC AND labels = "dmarq" AND "Dedupe Key" ~ "{dedupe_key}" AND statusCategory != Done' + }, + "create": { + "fields": { + "project": {"key": "EMAILSEC"}, + "issuetype": {"name": "Task"}, + "summary": "[DMARQ][{severity}] {title}", + "description": { + "type": "doc", + "version": 1, + "content": [ + { + "type": "paragraph", + "content": [ + { + "type": "text", + "text": "{detail}", + } + ], + }, + { + "type": "paragraph", + "content": [ + { + "type": "text", + "text": "Domain: {domain} | Compliance: {compliance_rate}% | Previous: {previous_compliance_rate}% | Drop: {drop_points} points", + } + ], + }, + ], + }, + "labels": ["dmarq", "email-security", "{alert_rule}"], + "priority": {"name": "High"}, + "customfield_dedupe_key": "{dedupe_key}", + } + }, + "update": { + "comment": "{event_time}: {detail}", + "fields": {"priority": {"name": "High"}}, + }, + "resolve": { + "transition": "Done", + "comment": "DMARQ reports this alert is resolved. Event: {event_id}", + }, +} + +GITHUB_ISSUE_TEMPLATE: Dict[str, Any] = { + "operation": "create_or_update_issue", + "repository": "security-operations/email-auth", + "lookup": { + "state": "open", + "labels": ["dmarq", "{alert_rule}", "dedupe:{dedupe_key}"], + }, + "create": { + "title": "[DMARQ][{severity}] {title}", + "body": "\n".join( + [ + "## Signal", + "{detail}", + "", + "## Evidence", + "- Domain: `{domain}`", + "- Compliance rate: `{compliance_rate}%`", + "- Previous compliance rate: `{previous_compliance_rate}%`", + "- Drop: `{drop_points}` points", + "- Failed messages: `{failed_count}` of `{message_count}`", + "", + "## Links", + "- Domain: {domain_url}", + "- Reports: {report_url}", + "", + "Dedupe key: `{dedupe_key}`", + ] + ), + "labels": ["dmarq", "email-security", "{alert_rule}", "dedupe:{dedupe_key}"], + }, + "update": {"comment": "{event_time}: {detail}\n\nDedupe key: `{dedupe_key}`"}, + "resolve": {"state": "closed", "comment": "Resolved by DMARQ event `{event_id}`."}, +} + +SLACK_MESSAGE_TEMPLATE: Dict[str, Any] = { + "channel": "#email-security", + "thread_key": "{dedupe_key}", + "text": "[DMARQ][{severity}] {title}", + "blocks": [ + { + "type": "header", + "text": {"type": "plain_text", "text": "DMARQ: {title}"}, + }, + { + "type": "section", + "text": {"type": "mrkdwn", "text": "{detail}"}, + }, + { + "type": "section", + "fields": [ + {"type": "mrkdwn", "text": "*Domain*\n{domain}"}, + {"type": "mrkdwn", "text": "*Severity*\n{severity}"}, + {"type": "mrkdwn", "text": "*Compliance*\n{compliance_rate}%"}, + {"type": "mrkdwn", "text": "*Drop*\n{drop_points} points"}, + ], + }, + { + "type": "actions", + "elements": [ + { + "type": "button", + "text": {"type": "plain_text", "text": "Open domain"}, + "url": "{domain_url}", + }, + { + "type": "button", + "text": {"type": "plain_text", "text": "Open reports"}, + "url": "{report_url}", + }, + ], + }, + ], +} + +TEAMS_MESSAGE_TEMPLATE: Dict[str, Any] = { + "type": "message", + "attachments": [ + { + "contentType": "application/vnd.microsoft.card.adaptive", + "content": { + "$schema": "http://adaptivecards.io/schemas/adaptive-card.json", + "type": "AdaptiveCard", + "version": "1.5", + "body": [ + { + "type": "TextBlock", + "size": "Medium", + "weight": "Bolder", + "text": "DMARQ: {title}", + }, + {"type": "TextBlock", "wrap": True, "text": "{detail}"}, + { + "type": "FactSet", + "facts": [ + {"title": "Domain", "value": "{domain}"}, + {"title": "Severity", "value": "{severity}"}, + {"title": "Compliance", "value": "{compliance_rate}%"}, + {"title": "Drop", "value": "{drop_points} points"}, + ], + }, + ], + "actions": [ + { + "type": "Action.OpenUrl", + "title": "Open domain", + "url": "{domain_url}", + }, + { + "type": "Action.OpenUrl", + "title": "Open reports", + "url": "{report_url}", + }, + ], + }, + } + ], +} + +OPERATING_MODEL = [ + "Use DMARQ webhook idempotency keys or the documented dedupe_key_template as the ticket correlation key.", + "Route ownership by event type: email-security for sender/compliance/alert signals, mail-operations for missing reports.", + "Create or update one ticket per active signal, and close or comment when a resolved event arrives.", + "Send chat notifications to a stable channel, then keep follow-up updates in the same thread whenever the destination supports threading.", + "Use severity and configured thresholds to suppress low-value repeats. Do not page on every aggregate report import.", + "Store Jira, GitHub, Slack, and Teams credentials in the receiving automation platform or secret manager, not in DMARQ payloads.", +] + + +def get_ticketing_chatops_templates() -> Dict[str, Any]: + """Return a copy of the ticketing and chatops template bundle.""" + return copy.deepcopy( + { + "template_version": TICKETING_CHATOPS_TEMPLATE_VERSION, + "schema_version": TICKETING_CHATOPS_SCHEMA_VERSION, + "event_types": WORKFLOW_EVENT_TYPES, + "event_workflow_mappings": EVENT_WORKFLOW_MAPPINGS, + "sample_context": SAMPLE_CONTEXT, + "payload_templates": { + "jira": JIRA_ISSUE_TEMPLATE, + "github": GITHUB_ISSUE_TEMPLATE, + "slack": SLACK_MESSAGE_TEMPLATE, + "microsoft_teams": TEAMS_MESSAGE_TEMPLATE, + }, + "operating_model": OPERATING_MODEL, + } + ) + + +def validate_workflow_template_bundle(bundle: Dict[str, Any]) -> List[str]: + """Validate bundled templates without pulling in a schema dependency.""" + errors: List[str] = [] + if bundle.get("schema_version") != TICKETING_CHATOPS_SCHEMA_VERSION: + errors.append("schema_version must be dmarq.workflow.template.v1") + if sorted(bundle.get("event_types", [])) != sorted(WORKFLOW_EVENT_TYPES): + errors.append("event_types must match supported workflow events") + + mappings = bundle.get("event_workflow_mappings") or {} + for event_type in WORKFLOW_EVENT_TYPES: + mapping = mappings.get(event_type) or {} + if not mapping.get("dedupe_key_template"): + errors.append(f"{event_type} is missing a dedupe key template") + if not mapping.get("owner"): + errors.append(f"{event_type} is missing an owner") + if not mapping.get("ticket_action"): + errors.append(f"{event_type} is missing a ticket action") + + templates = bundle.get("payload_templates") or {} + for destination in ["jira", "github", "slack", "microsoft_teams"]: + if destination not in templates: + errors.append(f"missing payload template: {destination}") + return errors diff --git a/backend/app/tests/test_ticketing_chatops_templates.py b/backend/app/tests/test_ticketing_chatops_templates.py new file mode 100644 index 0000000..26cb8e5 --- /dev/null +++ b/backend/app/tests/test_ticketing_chatops_templates.py @@ -0,0 +1,101 @@ +import json + +from fastapi.testclient import TestClient + +from app.services.ticketing_chatops_templates import ( + TICKETING_CHATOPS_SCHEMA_VERSION, + WORKFLOW_EVENT_TYPES, + get_ticketing_chatops_templates, + validate_workflow_template_bundle, +) +from app.services.webhook_events import EVENT_ALERT_RESOLVED, EVENT_COMPLIANCE_DROP + + +def _string_values(value): + if isinstance(value, dict): + for item in value.values(): + yield from _string_values(item) + elif isinstance(value, list): + for item in value: + yield from _string_values(item) + elif isinstance(value, str): + yield value + + +def test_ticketing_chatops_bundle_is_versioned_and_complete(): + """Workflow templates cover every ticketing/chatops event type.""" + bundle = get_ticketing_chatops_templates() + + assert bundle["schema_version"] == TICKETING_CHATOPS_SCHEMA_VERSION + assert sorted(bundle["event_types"]) == sorted(WORKFLOW_EVENT_TYPES) + assert validate_workflow_template_bundle(bundle) == [] + assert bundle["event_workflow_mappings"][EVENT_COMPLIANCE_DROP]["severity"] == "high" + assert ( + bundle["event_workflow_mappings"][EVENT_ALERT_RESOLVED]["ticket_action"] + == "resolve_or_comment" + ) + + +def test_ticketing_chatops_templates_have_no_embedded_credentials(): + """Examples must be safe to publish without leaking destination credentials.""" + bundle = get_ticketing_chatops_templates() + encoded_values = json.dumps(list(_string_values(bundle))).lower() + + for forbidden in ["xoxb-", "ghp_", "jira_pat", "incoming_webhook", "bearer "]: + assert forbidden not in encoded_values + + +def test_ticketing_chatops_validation_reports_template_drift(): + """Validator explains missing required workflow and destination fields.""" + bundle = get_ticketing_chatops_templates() + bundle["schema_version"] = "unexpected" + bundle["event_types"] = [EVENT_COMPLIANCE_DROP] + bundle["event_workflow_mappings"][EVENT_COMPLIANCE_DROP].pop("dedupe_key_template") + bundle["event_workflow_mappings"][EVENT_COMPLIANCE_DROP].pop("owner") + bundle["event_workflow_mappings"][EVENT_COMPLIANCE_DROP].pop("ticket_action") + bundle["payload_templates"].pop("slack") + + errors = validate_workflow_template_bundle(bundle) + + assert "schema_version must be dmarq.workflow.template.v1" in errors + assert "event_types must match supported workflow events" in errors + assert f"{EVENT_COMPLIANCE_DROP} is missing a dedupe key template" in errors + assert f"{EVENT_COMPLIANCE_DROP} is missing an owner" in errors + assert f"{EVENT_COMPLIANCE_DROP} is missing a ticket action" in errors + assert "missing payload template: slack" in errors + + +def test_ticketing_chatops_templates_return_independent_copies(): + """Mutating one returned bundle does not alter future callers.""" + bundle = get_ticketing_chatops_templates() + bundle["payload_templates"]["jira"]["operation"] = "mutated" + + fresh_bundle = get_ticketing_chatops_templates() + + assert fresh_bundle["payload_templates"]["jira"]["operation"] == "create_or_update_issue" + + +def test_ticketing_chatops_endpoint_returns_workflow_templates( + authed_client: TestClient, +): + """Administrators can fetch issue and chat message templates.""" + response = authed_client.get("/api/v1/integrations/ticketing-chatops/templates") + + assert response.status_code == 200 + body = response.json() + assert body["schema_version"] == TICKETING_CHATOPS_SCHEMA_VERSION + assert set(body["payload_templates"]) == { + "jira", + "github", + "slack", + "microsoft_teams", + } + assert body["payload_templates"]["jira"]["operation"] == "create_or_update_issue" + assert body["payload_templates"]["slack"]["thread_key"] == "{dedupe_key}" + + +def test_ticketing_chatops_endpoint_requires_admin_auth(client: TestClient): + """Workflow templates use the same admin boundary as integration settings.""" + response = client.get("/api/v1/integrations/ticketing-chatops/templates") + + assert response.status_code == 401 diff --git a/docs/milestones.md b/docs/milestones.md index 078ce9e..d0d36cb 100644 --- a/docs/milestones.md +++ b/docs/milestones.md @@ -230,7 +230,7 @@ Exit criteria: ## Milestone 14: Public API, Webhooks, and Core Integrations -Status: In progress +Status: Delivered Goal: let DMARQ integrate cleanly into existing security and operations workflows. @@ -238,7 +238,7 @@ Planned: - A stable, documented read-only API surface for posture and reporting queries. Delivered with scoped `reports:read`, `posture:read`, and `tls-reports:read` API tokens, public read-only endpoints, and per-token usage audit fields. - Webhook event delivery for key events (new sender source, compliance drop, missing reports, alert lifecycle). Delivered with encrypted webhook endpoints, signed delivery headers, idempotency keys, retry/backoff state, test sends, and delivery inspection. - SIEM integration templates delivered with a stable `dmarq.siem.event.v1` schema, source/compliance/alert examples, Splunk HEC, Elastic ECS, and Microsoft Sentinel ingestion shapes, and sensitive-field redaction guidance. -- Ticketing/chatops integration templates (Jira, GitHub, Slack, Teams). +- Ticketing/chatops integration templates delivered with event-to-workflow mappings, Jira/GitHub issue templates, Slack/Teams message templates, dedupe keys, ownership rules, and noise-control guidance. - Token/scoping model for API access that matches governance needs (service accounts, least privilege). Exit criteria: diff --git a/docs/reference/api.md b/docs/reference/api.md index 30e40c4..7798a64 100644 --- a/docs/reference/api.md +++ b/docs/reference/api.md @@ -554,8 +554,15 @@ DMARQ ships operator-ready templates for normalized SIEM ingestion: | Endpoint | Purpose | | --- | --- | | `GET /api/v1/integrations/siem/templates` | Return versioned SIEM schemas, examples, config hints, and redaction guidance | +| `GET /api/v1/integrations/ticketing-chatops/templates` | Return Jira, GitHub, Slack, Teams, mapping, dedupe, and operating-model templates | The SIEM template bundle includes the stable `dmarq.siem.event.v1` envelope, examples for sender, compliance-drop, and alert events, and ingestion shapes for Splunk HEC, Elastic ECS, and Microsoft Sentinel custom logs. See [SIEM Integration Templates](siem-integrations.md) for the full operator guide. + +The ticketing/chatops bundle includes event-to-workflow mappings, Jira and +GitHub issue templates, Slack and Microsoft Teams message templates, and +deduplication guidance. See +[Ticketing and Chatops Templates](ticketing-chatops-integrations.md) for the +full operator guide. diff --git a/docs/reference/ticketing-chatops-integrations.md b/docs/reference/ticketing-chatops-integrations.md new file mode 100644 index 0000000..9b94c30 --- /dev/null +++ b/docs/reference/ticketing-chatops-integrations.md @@ -0,0 +1,176 @@ +# Ticketing and Chatops Templates + +DMARQ publishes workflow templates for turning webhook events into tickets and +channel notifications. Use these templates with a relay, automation platform, or +SIEM rule that receives signed DMARQ webhook events. + +## Template Endpoint + +Administrators can fetch the template bundle from: + +```http +GET /api/v1/integrations/ticketing-chatops/templates +``` + +The response contains: + +- `schema_version`: stable workflow template identifier, currently `dmarq.workflow.template.v1`. +- `event_workflow_mappings`: event-to-owner, severity, action, and dedupe rules. +- `sample_context`: example values for rendering templates. +- `payload_templates`: Jira, GitHub Issues, Slack, and Microsoft Teams shapes. +- `operating_model`: ownership, dedupe, threading, and noise-control guidance. + +## Event Routing + +Use these default workflow mappings as the starting point: + +| Event | Owner | Ticket Action | Chat Action | +| --- | --- | --- | --- | +| `dmarq.sender.new` | email-security | create or update | notify channel | +| `dmarq.compliance.drop` | email-security | create or update | notify channel and thread | +| `dmarq.reports.missing` | mail-operations | create or update | notify channel | +| `dmarq.alert.created` | email-security | create or update | notify channel | +| `dmarq.alert.resolved` | email-security | resolve or comment | notify thread | + +Use the DMARQ webhook `X-DMARQ-Idempotency-Key` header when available. If the +receiver is building a workflow from API output, use the documented +`dedupe_key_template` from the template endpoint. + +## Jira + +Create or update one issue per active signal. Look up open issues by project, +label, and dedupe key before creating a new one. + +```json +{ + "operation": "create_or_update_issue", + "lookup": { + "jql": "project = EMAILSEC AND labels = \"dmarq\" AND \"Dedupe Key\" ~ \"{dedupe_key}\" AND statusCategory != Done" + }, + "create": { + "fields": { + "project": {"key": "EMAILSEC"}, + "issuetype": {"name": "Task"}, + "summary": "[DMARQ][{severity}] {title}", + "labels": ["dmarq", "email-security", "{alert_rule}"], + "priority": {"name": "High"}, + "customfield_dedupe_key": "{dedupe_key}" + } + }, + "update": { + "comment": "{event_time}: {detail}" + }, + "resolve": { + "transition": "Done", + "comment": "DMARQ reports this alert is resolved. Event: {event_id}" + } +} +``` + +Store Jira credentials in the relay, CI/CD variable store, or ticketing +automation platform. + +## GitHub Issues + +For teams tracking operations work in GitHub, create or update one issue in the +operations repository: + +```json +{ + "operation": "create_or_update_issue", + "repository": "security-operations/email-auth", + "lookup": { + "state": "open", + "labels": ["dmarq", "{alert_rule}", "dedupe:{dedupe_key}"] + }, + "create": { + "title": "[DMARQ][{severity}] {title}", + "labels": ["dmarq", "email-security", "{alert_rule}", "dedupe:{dedupe_key}"] + }, + "update": { + "comment": "{event_time}: {detail}\n\nDedupe key: `{dedupe_key}`" + }, + "resolve": { + "state": "closed", + "comment": "Resolved by DMARQ event `{event_id}`." + } +} +``` + +Use labels for routing and dedupe. Keep human assignment rules in GitHub or the +relay instead of hard-coding personal owners in DMARQ payloads. + +## Slack + +Send a concise summary to a stable channel, then keep updates in a thread keyed +by the dedupe key: + +```json +{ + "channel": "#email-security", + "thread_key": "{dedupe_key}", + "text": "[DMARQ][{severity}] {title}", + "blocks": [ + { + "type": "header", + "text": {"type": "plain_text", "text": "DMARQ: {title}"} + }, + { + "type": "section", + "text": {"type": "mrkdwn", "text": "{detail}"} + }, + { + "type": "section", + "fields": [ + {"type": "mrkdwn", "text": "*Domain*\n{domain}"}, + {"type": "mrkdwn", "text": "*Severity*\n{severity}"}, + {"type": "mrkdwn", "text": "*Compliance*\n{compliance_rate}%"}, + {"type": "mrkdwn", "text": "*Drop*\n{drop_points} points"} + ] + } + ] +} +``` + +Use channel mentions sparingly. Reserve paging or urgent mentions for +high-severity compliance drops or repeated missing-report windows. + +## Microsoft Teams + +Teams destinations can use an Adaptive Card: + +```json +{ + "type": "message", + "attachments": [ + { + "contentType": "application/vnd.microsoft.card.adaptive", + "content": { + "type": "AdaptiveCard", + "version": "1.5", + "body": [ + { + "type": "TextBlock", + "size": "Medium", + "weight": "Bolder", + "text": "DMARQ: {title}" + }, + {"type": "TextBlock", "wrap": true, "text": "{detail}"} + ] + } + } + ] +} +``` + +Use the relay or workflow platform to map dedupe keys to Teams threads when the +destination supports it. + +## Operating Model + +- Use one ticket per active signal and dedupe by DMARQ idempotency key or the documented dedupe key. +- Route sender, compliance, and alert signals to email security; route missing-report signals to mail operations. +- Close or comment on existing tickets when `dmarq.alert.resolved` arrives. +- Send chat updates into the same thread while a signal remains active. +- Keep repeats quiet: daily reminders are usually enough for missing reports, and compliance drops should follow configured thresholds. +- Keep Jira, GitHub, Slack, and Teams credentials in the receiving platform or secret manager, not in DMARQ payloads. diff --git a/docs/user_guide/settings.md b/docs/user_guide/settings.md index 125e0fa..ed37435 100644 --- a/docs/user_guide/settings.md +++ b/docs/user_guide/settings.md @@ -106,6 +106,11 @@ For SIEM pipelines, DMARQ also exposes a versioned template bundle at `dmarq.siem.event.v1` event schema, Splunk HEC, Elastic ECS, and Microsoft Sentinel examples, plus redaction guidance for sensitive fields. +For ticketing and chatops workflows, use +`/api/v1/integrations/ticketing-chatops/templates`. It includes event mappings, +Jira and GitHub issue templates, Slack and Microsoft Teams message templates, +dedupe keys, and recommended ownership/noise-control rules. + ## API Access DMARQ provides an API for integration with other systems: diff --git a/mkdocs.yml b/mkdocs.yml index 8345247..1a5c590 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -41,6 +41,7 @@ nav: - Technical Reference: - API Reference: reference/api.md - SIEM Integrations: reference/siem-integrations.md + - Ticketing and Chatops: reference/ticketing-chatops-integrations.md - Architecture: reference/architecture.md - Database Schema: reference/database.md - Development: