Skip to content

feat(gateway): per-route sender and event-type denylists for webhook routes - #18042

Closed
seanedwards wants to merge 1 commit into
NousResearch:mainfrom
seanedwards:feat/webhook-denylist-filters
Closed

feat(gateway): per-route sender and event-type denylists for webhook routes#18042
seanedwards wants to merge 1 commit into
NousResearch:mainfrom
seanedwards:feat/webhook-denylist-filters

Conversation

@seanedwards

@seanedwards seanedwards commented Apr 30, 2026

Copy link
Copy Markdown

Closes #18041.

Summary

Adds optional ignored_senders and ignored_event_types keys 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 recognizes X-Gitea-Event and X-Forgejo-Event headers 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

platforms:
  webhook:
    extra:
      routes:
        forgejo-firehose:
          secret: ${FORGEJO_WEBHOOK_SECRET}
          ignored_event_types: [status, push, watch, star]
          ignored_senders: [dependabot, forgejo-actions, mirror-sync-bot]
          prompt: ...

Design notes

  • Allowlist still wins. The existing 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 extraction is defensive. sender.login isn't formally documented as present on every Forgejo/Gitea event type, so a missing/non-dict sender falls through to dispatch rather than getting silently dropped.
  • Case-insensitive exact match. No glob/regex (YAGNI). Easy to add later if needed.
  • Filter placement. At the existing event-type filter seam (after auth, signature validation, parse, rate-limit). Keeps the change minimally invasive. One known consequence: filtered events count against the route's rate limit; the lever is the rate_limit config if it bites.

Test plan

  • 9 new unit tests in tests/gateway/test_webhook_adapter.py::TestDenylistFilters — both filters, case-insensitivity, missing-sender defensive default, allowlist precedence, Forgejo/Gitea header detection, backward compat
  • All 45 webhook-adapter tests pass; 27 related webhook integration/dynamic-route/signature tests still pass
  • Tested on Linux (Fedora 43, Python 3.12). Pure dict/string logic — no platform-specific behavior, no file I/O, no process management
  • Manual: configure a route with denylists, fire curl with various Forgejo payloads, confirm 200 {filtered: true} and no agent dispatch on hits, normal dispatch on misses

Out of scope (intentional)

  • Glob/regex sender matching — exact match covers the stated use case
  • Reordering the rate-limit step relative to the filter — keeping the change minimal
  • Startup-time schema validation for unknown route keys (e.g. typo'd ignored_sender singular silently no-ops) — worth a separate small PR

Files

  • gateway/platforms/webhook.py — header detection (Gitea/Forgejo) + two denylist checks at the existing filter seam
  • tests/gateway/test_webhook_adapter.pyTestDenylistFilters class with 9 tests
  • website/docs/user-guide/messaging/webhooks.md — route-properties table updated; filter response shape documented

🤖 Generated with Claude Code

…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>
@alt-glitch alt-glitch added type/feature New feature or request P3 Low — cosmetic, nice to have comp/gateway Gateway runner, session dispatch, delivery platform/webhook Webhook / API server labels Apr 30, 2026
@seanedwards

Copy link
Copy Markdown
Author

This is running and confirmed working in my homelab cluster.

@Kinkoolino-Hermes

Copy link
Copy Markdown
Contributor

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:

  • recognizing X-Gitea-Event / X-Forgejo-Event as first-class webhook event headers
  • being able to drop noisy firehose events by route via ignored_event_types / ignored_senders without invoking the agent

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 X-Forgejo-Event header path and event-type denylist behavior.

@teknium1

teknium1 commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

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 (equals/not_equals/contains/exists/... on payload fields, event type, and headers) plus optional route scripts for transform/narrow logic, wired through hermes webhook subscribe --filter/--script and config.yaml. Your use case should be covered by the generic filter syntax; if something specific is missing, please open a fresh issue against the new mechanism. Closing as superseded — appreciate the earlier push in this direction.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/gateway Gateway runner, session dispatch, delivery P3 Low — cosmetic, nice to have platform/webhook Webhook / API server type/feature New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(gateway): per-route sender and event-type denylists for webhook routes

4 participants