Repository navigation
Closed
Labels
enhancementNew feature or requestNew feature or requesthacktoberfestGood for Hacktoberfest contributorsGood for Hacktoberfest contributorshelp wantedExtra attention is neededExtra attention is needed
Description
Part of the October Challenge #997.
Why
Today a broken connector (expired credentials, changed upstream API) is only noticed when someone opens the Audit Log or a user complains that Claude "can't reach the ERP". Admins want a push: "connector SAP failed 5 times in 10 minutes", in Slack or in their own on-call tooling. Zapier, n8n and most API gateways ship failure notifications; we have nothing yet. This issue is the first, reviewable step: one webhook per organization, one alert rule, signed payloads.
Self-hosted vs Cloud
- Self-hosted: enabled in Community and Business. The webhook URL goes through the existing SSRF guard with the instance's normal policy, so
SSRF_ALLOWED_HOSTS/ the admin allowlist can permit an internal Mattermost or Alertmanager. - Cloud (
DEPLOYMENT_MODE=cloud): enabled, but stricter, because requests leave our infrastructure on behalf of a tenant:https:only, private/loopback/link-local (incl.169.254.169.254) always blocked, and the instance allowlist is ignored (on Cloud it contains internal hosts such asmotis, seedocker-compose.cloud.yml:138). No redirects followed. - Who can use it: org
ADMINonly (configure, test, delete). Settings are per organization; an alert only ever contains data from the organization the failing invocation belongs to.
What to build
- Settings storage in
OrgSettings(no Prisma migration needed), keyalert_webhook:{ url, format: 'json' | 'slack', threshold: number (default 5), windowMinutes: number (default 10), cooldownMinutes: number (default 60), enabled: boolean, secret }. Encryptsecretwithencrypt(secret, ENCRYPTION_KEY, aad = organizationId). - Admin API next to the SMTP endpoints,
ADMINonly:GET/PUT/DELETE /api/admin/settings/alert-webhook,POST /api/admin/settings/alert-webhook/test. The secret is generated server-side (32 random bytes, hex), returned once on create/rotate (PUT ... { rotateSecret: true }), never byGET. Validate the URL with the SSRF guard on save, not only on send. - Detection: in
AuditService.logInvocation, after the row is written and whenstatus !== 'SUCCESS'andorganizationIdandconnectorIdare set, call a newAlertsService.recordFailure(...)without awaiting it (fire-and-forget, errors logged, never thrown — the MCP tool call must not get slower or fail because of alerting). Count failures per(org, connector)in a sliding window: Redisincr+expirewhen connected, an in-memory map otherwise (Redis is optional, seeredis.service.ts:18). - Dedupe/throttle: after an alert fires for
(org, connector), suppress further alerts forcooldownMinutes. - Dispatch:
POSTwithContent-Type: application/json, 5 s timeout,maxRedirects: 0, one retry. Headers:X-AnythingMCP-Event: connector.failing,X-AnythingMCP-Timestamp: <unix>,X-AnythingMCP-Signature: sha256=<hex HMAC-SHA256(secret, "<timestamp>.<raw body>")>.jsonbody:{ event, organizationId, connector: { id, name }, failures, windowMinutes, lastError: { tool, message }, firstFailureAt, lastFailureAt, logsUrl }.slackbody:{ text }(works with Slack/Mattermost/Discord-compatible incoming webhooks).lastError.message: truncate to 300 chars. It can contain upstream response bodies; do not include tool input or output.
- UI: an "Alerts" card on
/settings/admin(admin-only page) with URL, format, threshold/window, enable toggle, "Send test", and a copy-once secret display.
Where to look
packages/backend/src/audit/audit.service.ts:21—logInvocation, the single place every tool call is recorded;:44status union (TIMEOUTexists in the enum too).packages/backend/src/mcp-server/dynamic-mcp-tools.ts:486— theERRORcall site (shows which fields are available:organizationId,connectorId,error).packages/backend/prisma/schema.prisma:78(OrgSettings),:678(ToolInvocation).packages/backend/src/settings/org-settings.service.ts:6andpackages/backend/src/settings/site-settings.controller.ts:113–180— the SMTP settings endpoints: the pattern to copy (ADMIN guard, secret never returned, test endpoint).packages/backend/src/common/crypto/encryption.util.ts:17—encrypt/decryptwith AAD.packages/backend/src/common/ssrf.util.ts:147(assertSafeOutboundUrl(url, env)),:43(readPolicy— env-driven, so on Cloud pass an env withSSRF_ALLOWED_HOSTS='',SSRF_ALLOW_PRIVATE/SSRF_ALLOW_LOCALHOSTunset),:369(createSsrfGuardedAgents(env)— connect-time check against DNS rebinding). Note: the DB allowlist provider (:76) is global; bypass it explicitly for webhooks on Cloud.packages/backend/src/common/guarded-http.util.ts:39— how axios is wired with guarded agents.packages/backend/src/common/redis.service.ts:86—incr/expire.packages/backend/src/common/deployment.service.ts:6—isCloud().packages/frontend/src/app/settings/admin/page.tsxandpackages/frontend/src/lib/api.ts:986(adminSettings) — UI and client to extend.
Acceptance criteria
- Unit specs next to the new service (
*.spec.ts, run withnpm test --workspace=packages/backend): threshold reached → one dispatch; cooldown suppresses the second; success calls never count; other orgs' failures never count; dispatch errors never rejectlogInvocation. - Signature spec: a receiver can verify
sha256=with the documented algorithm; secret never appears inGETresponses or logs. - SSRF spec (with
SSRF_GUARD=enabled, seessrf.util.spec.ts): on Cloudhttp://,http://169.254.169.254,https://10.0.0.1and an allowlisted host (motis) are rejected at save and at send. - Non-admin gets 403 on all endpoints (
roles.guard.ts:11). - Docs: new section in
docs/operations/observability.md(payload, headers, verification snippet in Node).
Out of scope
- Other events (license expiry, OAuth token refresh failure, KG jobs) — follow-up issues, reuse the same dispatcher.
- Multiple webhooks per org, per-connector rules, email alerts, PagerDuty/Opsgenie native formats.
- A delivery log / retry queue UI.
- Anything under
ee/(do not add code there).
Size
L (several days, may be split: backend detection+dispatch first, UI second).
How to claim
Comment "I'd like to work on this" and we'll assign you. Rules in #997.
Metadata
Metadata
Assignees
Labels
enhancementNew feature or requestNew feature or requesthacktoberfestGood for Hacktoberfest contributorsGood for Hacktoberfest contributorshelp wantedExtra attention is neededExtra attention is needed
Activity
helpcodeai commentedon Oct 10, 2026
@rangarajan19 this is the new issue for the one you are working on, and it is still yours. Leave a short comment here so GitHub lets us assign it to you again. The notes from before, for reference:
rangarajan19 commentedon Oct 10, 2026
Thanks for carrying this over @helpcodeai. Still on it — PR 1 (backend) is already up and merged as #976. Working on PR 2 (the Alerts card on /settings/admin) next.
rangarajan19 commentedon Oct 10, 2026
PR 2 (UI) is up: #1019 — @helpcodeai
feat(settings): add the Connector Failure Alerts card to admin settin…
helpcodeai commentedon Oct 14, 2026
Both parts are merged (#976 for the backend, #1019 for the Alerts card), so I'm closing this. Thanks @rangarajan19!