feat(gateway): per-route sender and event-type denylists for webhook routes - #18042
feat(gateway): per-route sender and event-type denylists for webhook routes#18042seanedwards wants to merge 1 commit into
Conversation
…ooks
Adds optional `ignored_senders` and `ignored_event_types` keys to webhook
route config, letting Hermes drop noisy events from a Forgejo (or
Gitea/GitHub) firehose without invoking the agent. Also recognizes the
`X-Gitea-Event` and `X-Forgejo-Event` headers for event-type detection.
Filtered events return 200 with `{filtered, reason, ...}` so the source
sees success and won't retry. Allowlist (`events`) still wins; both new
keys default to empty for backward compatibility.
Tested on Linux (Fedora 43, Python 3.12) — pure dict/string logic with
no platform-specific behavior.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
This is running and confirmed working in my homelab cluster. |
|
Thanks for this PR — this matches a real Forgejo/Gitea webhook routing need. In a self-hosted Hermes setup, the two most useful parts here are:
That combination would allow a safe Forgejo webhook route that can accept broad repository events but only pass the intended subset to Hermes. Returning a successful filtered response is also important because Forgejo/Gitea should not retry intentionally ignored events. I can test this against a Forgejo webhook route if it helps, especially the |
|
Thanks for this PR — webhook payload filtering has now landed on main via #60944 (salvage of @evelynburger's #57544), which generalizes this: route-level payload filters ( |
Closes #18041.
Summary
Adds optional
ignored_sendersandignored_event_typeskeys to webhook route config so a system-wide Forgejo/Gitea/GitHub firehose can drop noisy events (status,push,dependabot, etc.) without invoking the agent. Also recognizesX-Gitea-EventandX-Forgejo-Eventheaders alongside the existing GitHub/GitLab detection.Filtered events return
200 {filtered: true, reason, ...}so the source treats the request as successful and won't retry. Both new keys default to empty — fully backward-compatible.Example
Design notes
events:allowlist runs first (returning{status: ignored}), then the new denylists run (returning{filtered: true, ...}). This preserves backward compatibility and gives different response shapes for "config rejected this" vs "denylist filtered this," which helps when debugging overlapping configs.sender.loginisn't formally documented as present on every Forgejo/Gitea event type, so a missing/non-dictsenderfalls through to dispatch rather than getting silently dropped.rate_limitconfig if it bites.Test plan
tests/gateway/test_webhook_adapter.py::TestDenylistFilters— both filters, case-insensitivity, missing-sender defensive default, allowlist precedence, Forgejo/Gitea header detection, backward compatcurlwith various Forgejo payloads, confirm200 {filtered: true}and no agent dispatch on hits, normal dispatch on missesOut of scope (intentional)
ignored_sendersingular silently no-ops) — worth a separate small PRFiles
gateway/platforms/webhook.py— header detection (Gitea/Forgejo) + two denylist checks at the existing filter seamtests/gateway/test_webhook_adapter.py—TestDenylistFiltersclass with 9 testswebsite/docs/user-guide/messaging/webhooks.md— route-properties table updated; filter response shape documented🤖 Generated with Claude Code