Skip to content

Scope webhooks per github-app with optional signature validation - #58

Merged
nresare merged 1 commit into
mainfrom
webhook-per-app-validation
Jul 16, 2026
Merged

Scope webhooks per github-app with optional signature validation#58
nresare merged 1 commit into
mainfrom
webhook-per-app-validation

Conversation

@nresare

@nresare nresare commented Jul 16, 2026

Copy link
Copy Markdown
Collaborator

What

Reworks GitHub webhook handling from a single global endpoint into a per-github-app feature, and adds optional validation of incoming webhook deliveries.

  • Callback URL encodes the app: POST /webhook/{github-app} (e.g. /webhook/deployments), consistent with the existing /installation-token/{github-app}/... and /proxy/{github-app}/... routes. The handler resolves the named app first and returns 404 for an unknown one.
  • webhook-target moved into [[github-app]]: each app opts into bridging independently. The [nats] connection block stays top-level and is shared; idcat connects to NATS when any app sets webhook-target = "nats".
  • New optional webhook-validation-secret-file on [[github-app]]: when set, idcat verifies the X-Hub-Signature-256 header (HMAC-SHA256 over the raw body, constant-time compare) following GitHub's documented scheme and rejects mismatches (and missing signatures) with 401. The secret is read from the file per delivery, so it can be rotated without restarting idcat.

Why

Previously all webhooks hit one /webhook endpoint with a single global webhook-target, so there was no way to bridge some apps but not others, and no way to authenticate that a delivery genuinely came from GitHub. Encoding the app in the URL lets idcat select the right config — including the right validation secret — per delivery.

Notes for reviewers

  • Adds hmac and hex dependencies (pinned to versions matching the existing sha2 = "0.10").
  • Signature validation is independent of bridging: an app may validate deliveries without setting webhook-target.
  • Validation covers config parsing/validation, unknown-app rejection, and missing/valid/tampered-signature handling; the signature test uses GitHub's documented example vector. cargo fmt --check, cargo clippy --all-targets, and cargo test all pass.
  • README.md and idcat.toml.example document the new URL scheme and options.

Webhook handling was a single global endpoint with a top-level
webhook-target. This reworks it to be per github-app so different apps
can be bridged (or not) independently, and adds optional validation of
GitHub webhook deliveries.

- Callback URL now encodes the app: POST /webhook/{github-app}. The
  handler resolves the named app (404 if unknown) before doing anything.
- Move webhook-target from the top-level config into the [[github-app]]
  block. The [nats] connection stays top-level and is shared by every
  app that opts in; idcat connects to NATS when any app sets
  webhook-target = "nats".
- Add optional webhook-validation-secret-file to [[github-app]]. When
  set, idcat verifies the X-Hub-Signature-256 header (HMAC-SHA256 over
  the raw body, constant-time compare) per GitHub's documented scheme
  and rejects mismatches with 401. The secret is read per delivery so it
  can be rotated without a restart.

Adds hmac and hex dependencies (matching the existing sha2 0.10).
@nresare
nresare force-pushed the webhook-per-app-validation branch from fbde66d to 4034eab Compare July 16, 2026 12:16
@nresare
nresare merged commit 73c7599 into main Jul 16, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant