Datadog, PagerDuty and CloudWatch adapters
## Requirement (verbatim)
2. "Monitor adapters (bring your own): new trigger source kinds `sentry`, `datadog`, `pagerduty`, `cloudwatch` (HTTP ingress with signature verification, beside triggers.py SOURCE_KINDS) normalised into an `incidents` table: fingerprint, first/last seen, count, severity, environment, stack frames, release/commit when sent. One row per fingerprint; recurrences update, never duplicate. Adapters count against the org's trigger quota."
Settled: D4 (all four + generic); D6 quota clause dropped. A5: "Where a vendor offers no signing scheme (Datadog webhooks), 'signature verification' means a per-trigger shared-secret header, verified in constant time. The vendor's real signing scheme (Sentry, PagerDuty, SNS) is used wherever one exists" — confirm each from vendor docs, never memory. A6: CloudWatch = HTTPS SNS subscription endpoint; `alarm_relay` unchanged. Relaxed AC-11 (A-11, flagged for Finish): adapters file nothing; ingress gets exactly a webhook's limits; no org quota.
Design: architecture artifact of Task 12249, §5.2, §5.3 rows datadog/pagerduty/cloudwatch, fingerprints, `metrics` shape (validate only), §13 risk 11. Framework from the Sentry adapter child.
## Scope (architecture C8)
- `datadog`: header `X-Jaah-Token` shared secret, constant time; P1→critical, P2→error, else warning; resolve on `$ALERT_TRANSITION` = Recovered; fingerprint `$AGGREG_KEY`; dedup `$ID`; config `metrics?`.
- `pagerduty`: `X-PagerDuty-Signature`, any comma-separated `v1=` hex HMAC-SHA256(raw body, subscription secret); urgency high→critical, low→warning; `incident.resolved` resolves, `incident.reopened` = occurrence; fingerprint `incident_key` else incident id; dedup `event.id`.
- `cloudwatch`: SNS SignatureVersion 1 (SHA1) and 2 (SHA256); signing cert and `SubscribeURL` fetched only over HTTPS from `sns.<region>.amazonaws.com` (host-pinned; SSRF guard); `TopicArn` ∈ `topic_arns[]` (required); `SubscriptionConfirmation` confirmed by GET `SubscribeURL` (normalise returns []); severity from config (default `error`); `NewStateValue: OK` resolves; fingerprint `AlarmName|Region|AWSAccountId`; dedup `x-amz-sns-message-id`.
- Each kind in `SOURCE_KINDS`/`HTTP_SOURCE_KINDS`, labels, `SHAPES` configs, web picker; module docstring with field map + doc URL; fixtures = vendors' documented samples.
Credentials & reach:
- Adapter deliveries (AC-7..11) — none — signed `curl` with the trigger's own Jaah-held secret — **verified**
- UI checks on dev — `org/console/dev-login` (org Admin), `https://dev.jaah.ai` — credential **verified**; environment **deferred** for this node (operator R4: run from a tailnet node)
- Sentry/Datadog pulls, vendor "send test" — no `sentry`/`datadog`/`pagerduty` credential — **deferred** — operator R4: fixture tests with vendor sample payloads
- AC-29 gates — worktree only — **verified**
Out of scope: Do NOT change `alarm_relay.py`'s Slack paging (A6). Do NOT add an org trigger quota (D6). Do NOT store a monitor credential or webhook secret anywhere but the existing trigger secret and credential stores. Do NOT build metric pulls (regression child). Epic out of scope (verbatim): "building an APM or log store; replacing the customer's monitor; auto-merging a revert without the project's policy; any scoring or ranking logic (the outcome ledger epic reads the states this epic writes)."
Done means:
- AC-7 (these kinds): "all four can be created on dev, and each one's vendor 'send test' or signed sample produces a `received` → processed delivery row." — console create + signed sample for datadog, pagerduty, cloudwatch.
- AC-8: "an unsigned or wrongly signed request to each adapter's ingress gets 401 and writes no incident. ... CloudWatch: an SNS SubscriptionConfirmation is confirmed, and a message with a bad SNS signature is refused (A6). Datadog: the shared-secret header is required (A5)." — tests incl. SNS cert from a non-AWS host refused, TopicArn not in list refused, SignatureVersion 1 and 2 both verified.
- AC-9: "Datadog, PagerDuty and CloudWatch each produce one row with their own fields mapped. Fields a vendor does not send show as 'not sent' (never blank and never a fake value)." — fixture tests.
- AC-10: occurrence twice → one row, resolve → resolved, again → reopened, per kind — tests.
- AC-11 (relaxed, quoted above): existing rate-limit tests extended to the three kinds.
- AC-29: `./dc check quick` green; changed-area tests green; merge lane full gate green.
Activity
- Draft
- Ready