Skip to content

feat: add dependency-free Go webhook receiver example with Standard Webhooks signature verification - #5268

Merged
Pratham-Mishra04 merged 1 commit into
devfrom
07-16-docs_add_self-contained_webhook_receiver_example
Jul 17, 2026
Merged

Pratham-Mishra04 merged 1 commit into
devfrom
07-16-docs_add_self-contained_webhook_receiver_example

Conversation

@Pratham-Mishra04

Copy link
Copy Markdown
Collaborator

Summary

Adds a minimal, dependency-free Go example receiver for Bifrost async inference webhooks. The example demonstrates the critical requirement for any webhook receiver: verifying the Standard Webhooks HMAC-SHA256 signature before trusting a delivery.

Changes

  • Added examples/webhooks/main.go implementing a webhook receiver with:
    • verify — validates the webhook-id, webhook-timestamp, and webhook-signature headers, enforces a 5-minute replay tolerance window, and accepts any matching signature from a space-separated list (supporting secret rotation)
    • sign — recomputes the v1,<base64> HMAC-SHA256 signature over {id}.{timestamp}.{body} using the whsec_-prefixed secret, mirroring Bifrost's own signer exactly
    • An HTTP handler that reads the body, verifies the signature, unmarshals the event envelope, and returns 204 No Content on success
    • A /healthz endpoint alongside POST /webhook
  • Added examples/webhooks/main_test.go pinning the canonical Standard Webhooks reference vector (the same vector Bifrost's signer pins), plus table-driven rejection tests covering tampered bodies, wrong IDs, forged signatures, stale timestamps, and missing headers
  • Added examples/webhooks/README.md documenting the wire format, signature algorithm, deduplication requirement, and how to run and test the example

Type of change

  • Bug fix
  • Feature
  • Refactor
  • Documentation
  • Chore/CI

Affected areas

  • Core (Go)
  • Transports (HTTP)
  • Providers/Integrations
  • Plugins
  • UI (React)
  • Docs

How to test

cd examples/webhooks
go test ./...

To run the receiver against a live Bifrost instance:

WEBHOOK_SECRET=whsec_your_secret_here go run .

The receiver listens on :8080 by default (override with ADDR). Point a Bifrost webhook endpoint at http://<host>:8080/webhook and complete an async job to see verified deliveries logged.

Environment variables:

Variable Required Default Description
WEBHOOK_SECRET Yes Endpoint signing secret (whsec_...)
ADDR No :8080 Address the receiver listens on

Screenshots/Recordings

N/A

Breaking changes

  • No

Related issues

N/A

Security considerations

  • Signatures are compared using crypto/subtle.ConstantTimeCompare to prevent timing-based secret leakage.
  • The timestamp tolerance window (5 minutes) blunts replay attacks.
  • The request body is capped at 1 MiB (io.LimitReader) to prevent memory exhaustion from malicious senders.
  • Verification failure reasons are logged server-side but not echoed in the HTTP response, avoiding attacker probing.
  • The whsec_ signing secret is read from an environment variable; the README explicitly warns never to hard-code it.

Checklist

  • I read docs/contributing/README.md and followed the guidelines
  • I added/updated tests where appropriate
  • I updated documentation where needed
  • I verified builds succeed (Go and UI)
  • I verified the CI pipeline passes locally if applicable

@CLAassistant

Copy link
Copy Markdown

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.
You have signed the CLA already but the status is still pending? Let us recheck it.

@coderabbitai

coderabbitai Bot commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

You’ve reached a temporary PR review limit under our Fair Usage Limits Policy.

Your recent review volume is higher than typical usage, so adaptive limits are currently applied.

Next review available in: 33 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: b88e6548-7115-4a07-84b1-8011301bbe21

📥 Commits

Reviewing files that changed from the base of the PR and between c30deda and 5039720.

📒 Files selected for processing (4)
  • examples/webhooks/README.md
  • examples/webhooks/go.mod
  • examples/webhooks/main.go
  • examples/webhooks/main_test.go
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch 07-16-docs_add_self-contained_webhook_receiver_example

Comment @coderabbitai help to get the list of available commands.

Pratham-Mishra04 commented Jul 15, 2026

Copy link
Copy Markdown
Collaborator Author

This stack of pull requests is managed by Graphite. Learn more about stacking.

@greptile-apps

greptile-apps Bot commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

Confidence Score: 5/5

This looks safe to merge.

  • Oversized requests are detected instead of being silently truncated.
  • Slow or stalled connections are bounded by server timeouts.
  • No blocking issue remains in the updated code.

Important Files Changed

Filename Overview
examples/webhooks/main.go Adds the webhook receiver, verification logic, request limits, required-header checks, and server timeouts.
examples/webhooks/main_test.go Adds reference-vector and rejection tests for signatures and required headers.
examples/webhooks/README.md Documents webhook payloads, verification, deduplication, configuration, and local execution.
examples/webhooks/go.mod Defines the standalone standard-library-only Go module.

Reviews (4): Last reviewed commit: "docs: add self-contained webhook receive..." | Re-trigger Greptile

Comment thread examples/webhooks/main.go Outdated
Comment thread examples/webhooks/main.go Outdated
@Pratham-Mishra04
Pratham-Mishra04 marked this pull request as draft July 16, 2026 09:05
@Pratham-Mishra04
Pratham-Mishra04 force-pushed the 07-16-test_async_webhooks_e2e_suite branch from 89461a9 to 9e6981f Compare July 16, 2026 09:41
@Pratham-Mishra04
Pratham-Mishra04 force-pushed the 07-16-docs_add_self-contained_webhook_receiver_example branch from 88d9468 to 254c7eb Compare July 16, 2026 09:41
coderabbitai[bot]
coderabbitai Bot previously approved these changes Jul 16, 2026
@Pratham-Mishra04
Pratham-Mishra04 force-pushed the 07-16-test_async_webhooks_e2e_suite branch from 9e6981f to 9caf331 Compare July 16, 2026 13:04
@Pratham-Mishra04
Pratham-Mishra04 force-pushed the 07-16-docs_add_self-contained_webhook_receiver_example branch 2 times, most recently from 7cefd96 to b32fc1a Compare July 17, 2026 05:29
@Pratham-Mishra04
Pratham-Mishra04 force-pushed the 07-16-test_async_webhooks_e2e_suite branch from 9caf331 to 8290da0 Compare July 17, 2026 05:29
@Pratham-Mishra04
Pratham-Mishra04 marked this pull request as ready for review July 17, 2026 06:04
@Pratham-Mishra04
Pratham-Mishra04 force-pushed the 07-16-docs_add_self-contained_webhook_receiver_example branch from b32fc1a to 7aaf2ad Compare July 17, 2026 06:13
@Pratham-Mishra04
Pratham-Mishra04 force-pushed the 07-16-test_async_webhooks_e2e_suite branch from 8290da0 to 419f933 Compare July 17, 2026 06:13

Pratham-Mishra04 commented Jul 17, 2026

Copy link
Copy Markdown
Collaborator Author

Merge activity

  • Jul 17, 8:36 AM UTC: A user started a stack merge that includes this pull request via Graphite.
  • Jul 17, 9:09 AM UTC: Graphite rebased this pull request as part of a merge.
  • Jul 17, 9:10 AM UTC: @Pratham-Mishra04 merged this pull request with Graphite.

@Pratham-Mishra04
Pratham-Mishra04 changed the base branch from 07-16-test_async_webhooks_e2e_suite to graphite-base/5268 July 17, 2026 09:05
@Pratham-Mishra04
Pratham-Mishra04 changed the base branch from graphite-base/5268 to dev July 17, 2026 09:08
@Pratham-Mishra04
Pratham-Mishra04 dismissed coderabbitai[bot]’s stale review July 17, 2026 09:08

The base branch was changed.

@Pratham-Mishra04
Pratham-Mishra04 requested a review from a team as a code owner July 17, 2026 09:08
@Pratham-Mishra04
Pratham-Mishra04 force-pushed the 07-16-docs_add_self-contained_webhook_receiver_example branch from 7aaf2ad to 5039720 Compare July 17, 2026 09:08
@Pratham-Mishra04
Pratham-Mishra04 merged commit c631725 into dev Jul 17, 2026
14 of 15 checks passed
@Pratham-Mishra04
Pratham-Mishra04 deleted the 07-16-docs_add_self-contained_webhook_receiver_example branch July 17, 2026 09:10
akshaydeo pushed a commit that referenced this pull request Jul 17, 2026
…ebhooks signature verification (#5268)

## Summary

Adds a minimal, dependency-free Go example receiver for Bifrost async inference webhooks. The example demonstrates the critical requirement for any webhook receiver: verifying the Standard Webhooks HMAC-SHA256 signature before trusting a delivery.

## Changes

- Added `examples/webhooks/main.go` implementing a webhook receiver with:
  - `verify` — validates the `webhook-id`, `webhook-timestamp`, and `webhook-signature` headers, enforces a 5-minute replay tolerance window, and accepts any matching signature from a space-separated list (supporting secret rotation)
  - `sign` — recomputes the `v1,<base64>` HMAC-SHA256 signature over `{id}.{timestamp}.{body}` using the `whsec_`-prefixed secret, mirroring Bifrost's own signer exactly
  - An HTTP handler that reads the body, verifies the signature, unmarshals the event envelope, and returns `204 No Content` on success
  - A `/healthz` endpoint alongside `POST /webhook`
- Added `examples/webhooks/main_test.go` pinning the canonical Standard Webhooks reference vector (the same vector Bifrost's signer pins), plus table-driven rejection tests covering tampered bodies, wrong IDs, forged signatures, stale timestamps, and missing headers
- Added `examples/webhooks/README.md` documenting the wire format, signature algorithm, deduplication requirement, and how to run and test the example

## Type of change

- [ ] Bug fix
- [ ] Feature
- [ ] Refactor
- [x] Documentation
- [ ] Chore/CI

## Affected areas

- [ ] Core (Go)
- [ ] Transports (HTTP)
- [ ] Providers/Integrations
- [ ] Plugins
- [ ] UI (React)
- [x] Docs

## How to test

```sh
cd examples/webhooks
go test ./...
```

To run the receiver against a live Bifrost instance:

```sh
WEBHOOK_SECRET=whsec_your_secret_here go run .
```

The receiver listens on `:8080` by default (override with `ADDR`). Point a Bifrost webhook endpoint at `http://<host>:8080/webhook` and complete an async job to see verified deliveries logged.

**Environment variables:**

| Variable         | Required | Default | Description                                      |
|------------------|----------|---------|--------------------------------------------------|
| `WEBHOOK_SECRET` | Yes      | —       | Endpoint signing secret (`whsec_...`)            |
| `ADDR`           | No       | `:8080` | Address the receiver listens on                  |

## Screenshots/Recordings

N/A

## Breaking changes

- [x] No

## Related issues

N/A

## Security considerations

- Signatures are compared using `crypto/subtle.ConstantTimeCompare` to prevent timing-based secret leakage.
- The timestamp tolerance window (5 minutes) blunts replay attacks.
- The request body is capped at 1 MiB (`io.LimitReader`) to prevent memory exhaustion from malicious senders.
- Verification failure reasons are logged server-side but not echoed in the HTTP response, avoiding attacker probing.
- The `whsec_` signing secret is read from an environment variable; the README explicitly warns never to hard-code it.

## Checklist

- [x] I read `docs/contributing/README.md` and followed the guidelines
- [x] I added/updated tests where appropriate
- [x] I updated documentation where needed
- [x] I verified builds succeed (Go and UI)
- [ ] I verified the CI pipeline passes locally if applicable
akshaydeo pushed a commit that referenced this pull request Jul 18, 2026
…ebhooks signature verification (#5268)

## Summary

Adds a minimal, dependency-free Go example receiver for Bifrost async inference webhooks. The example demonstrates the critical requirement for any webhook receiver: verifying the Standard Webhooks HMAC-SHA256 signature before trusting a delivery.

## Changes

- Added `examples/webhooks/main.go` implementing a webhook receiver with:
  - `verify` — validates the `webhook-id`, `webhook-timestamp`, and `webhook-signature` headers, enforces a 5-minute replay tolerance window, and accepts any matching signature from a space-separated list (supporting secret rotation)
  - `sign` — recomputes the `v1,<base64>` HMAC-SHA256 signature over `{id}.{timestamp}.{body}` using the `whsec_`-prefixed secret, mirroring Bifrost's own signer exactly
  - An HTTP handler that reads the body, verifies the signature, unmarshals the event envelope, and returns `204 No Content` on success
  - A `/healthz` endpoint alongside `POST /webhook`
- Added `examples/webhooks/main_test.go` pinning the canonical Standard Webhooks reference vector (the same vector Bifrost's signer pins), plus table-driven rejection tests covering tampered bodies, wrong IDs, forged signatures, stale timestamps, and missing headers
- Added `examples/webhooks/README.md` documenting the wire format, signature algorithm, deduplication requirement, and how to run and test the example

## Type of change

- [ ] Bug fix
- [ ] Feature
- [ ] Refactor
- [x] Documentation
- [ ] Chore/CI

## Affected areas

- [ ] Core (Go)
- [ ] Transports (HTTP)
- [ ] Providers/Integrations
- [ ] Plugins
- [ ] UI (React)
- [x] Docs

## How to test

```sh
cd examples/webhooks
go test ./...
```

To run the receiver against a live Bifrost instance:

```sh
WEBHOOK_SECRET=whsec_your_secret_here go run .
```

The receiver listens on `:8080` by default (override with `ADDR`). Point a Bifrost webhook endpoint at `http://<host>:8080/webhook` and complete an async job to see verified deliveries logged.

**Environment variables:**

| Variable         | Required | Default | Description                                      |
|------------------|----------|---------|--------------------------------------------------|
| `WEBHOOK_SECRET` | Yes      | —       | Endpoint signing secret (`whsec_...`)            |
| `ADDR`           | No       | `:8080` | Address the receiver listens on                  |

## Screenshots/Recordings

N/A

## Breaking changes

- [x] No

## Related issues

N/A

## Security considerations

- Signatures are compared using `crypto/subtle.ConstantTimeCompare` to prevent timing-based secret leakage.
- The timestamp tolerance window (5 minutes) blunts replay attacks.
- The request body is capped at 1 MiB (`io.LimitReader`) to prevent memory exhaustion from malicious senders.
- Verification failure reasons are logged server-side but not echoed in the HTTP response, avoiding attacker probing.
- The `whsec_` signing secret is read from an environment variable; the README explicitly warns never to hard-code it.

## Checklist

- [x] I read `docs/contributing/README.md` and followed the guidelines
- [x] I added/updated tests where appropriate
- [x] I updated documentation where needed
- [x] I verified builds succeed (Go and UI)
- [ ] I verified the CI pipeline passes locally if applicable
akhsaul pushed a commit to akhsaul/bifrost that referenced this pull request Aug 27, 2026
…ebhooks signature verification (maximhq#5268)

## Summary

Adds a minimal, dependency-free Go example receiver for Bifrost async inference webhooks. The example demonstrates the critical requirement for any webhook receiver: verifying the Standard Webhooks HMAC-SHA256 signature before trusting a delivery.

## Changes

- Added `examples/webhooks/main.go` implementing a webhook receiver with:
  - `verify` — validates the `webhook-id`, `webhook-timestamp`, and `webhook-signature` headers, enforces a 5-minute replay tolerance window, and accepts any matching signature from a space-separated list (supporting secret rotation)
  - `sign` — recomputes the `v1,<base64>` HMAC-SHA256 signature over `{id}.{timestamp}.{body}` using the `whsec_`-prefixed secret, mirroring Bifrost's own signer exactly
  - An HTTP handler that reads the body, verifies the signature, unmarshals the event envelope, and returns `204 No Content` on success
  - A `/healthz` endpoint alongside `POST /webhook`
- Added `examples/webhooks/main_test.go` pinning the canonical Standard Webhooks reference vector (the same vector Bifrost's signer pins), plus table-driven rejection tests covering tampered bodies, wrong IDs, forged signatures, stale timestamps, and missing headers
- Added `examples/webhooks/README.md` documenting the wire format, signature algorithm, deduplication requirement, and how to run and test the example

## Type of change

- [ ] Bug fix
- [ ] Feature
- [ ] Refactor
- [x] Documentation
- [ ] Chore/CI

## Affected areas

- [ ] Core (Go)
- [ ] Transports (HTTP)
- [ ] Providers/Integrations
- [ ] Plugins
- [ ] UI (React)
- [x] Docs

## How to test

```sh
cd examples/webhooks
go test ./...
```

To run the receiver against a live Bifrost instance:

```sh
WEBHOOK_SECRET=whsec_your_secret_here go run .
```

The receiver listens on `:8080` by default (override with `ADDR`). Point a Bifrost webhook endpoint at `http://<host>:8080/webhook` and complete an async job to see verified deliveries logged.

**Environment variables:**

| Variable         | Required | Default | Description                                      |
|------------------|----------|---------|--------------------------------------------------|
| `WEBHOOK_SECRET` | Yes      | —       | Endpoint signing secret (`whsec_...`)            |
| `ADDR`           | No       | `:8080` | Address the receiver listens on                  |

## Screenshots/Recordings

N/A

## Breaking changes

- [x] No

## Related issues

N/A

## Security considerations

- Signatures are compared using `crypto/subtle.ConstantTimeCompare` to prevent timing-based secret leakage.
- The timestamp tolerance window (5 minutes) blunts replay attacks.
- The request body is capped at 1 MiB (`io.LimitReader`) to prevent memory exhaustion from malicious senders.
- Verification failure reasons are logged server-side but not echoed in the HTTP response, avoiding attacker probing.
- The `whsec_` signing secret is read from an environment variable; the README explicitly warns never to hard-code it.

## Checklist

- [x] I read `docs/contributing/README.md` and followed the guidelines
- [x] I added/updated tests where appropriate
- [x] I updated documentation where needed
- [x] I verified builds succeed (Go and UI)
- [ ] I verified the CI pipeline passes locally if applicable
occcat pushed a commit to occcat/bifrost that referenced this pull request Sep 2, 2026
…ebhooks signature verification (maximhq#5268)

## Summary

Adds a minimal, dependency-free Go example receiver for Bifrost async inference webhooks. The example demonstrates the critical requirement for any webhook receiver: verifying the Standard Webhooks HMAC-SHA256 signature before trusting a delivery.

## Changes

- Added `examples/webhooks/main.go` implementing a webhook receiver with:
  - `verify` — validates the `webhook-id`, `webhook-timestamp`, and `webhook-signature` headers, enforces a 5-minute replay tolerance window, and accepts any matching signature from a space-separated list (supporting secret rotation)
  - `sign` — recomputes the `v1,<base64>` HMAC-SHA256 signature over `{id}.{timestamp}.{body}` using the `whsec_`-prefixed secret, mirroring Bifrost's own signer exactly
  - An HTTP handler that reads the body, verifies the signature, unmarshals the event envelope, and returns `204 No Content` on success
  - A `/healthz` endpoint alongside `POST /webhook`
- Added `examples/webhooks/main_test.go` pinning the canonical Standard Webhooks reference vector (the same vector Bifrost's signer pins), plus table-driven rejection tests covering tampered bodies, wrong IDs, forged signatures, stale timestamps, and missing headers
- Added `examples/webhooks/README.md` documenting the wire format, signature algorithm, deduplication requirement, and how to run and test the example

## Type of change

- [ ] Bug fix
- [ ] Feature
- [ ] Refactor
- [x] Documentation
- [ ] Chore/CI

## Affected areas

- [ ] Core (Go)
- [ ] Transports (HTTP)
- [ ] Providers/Integrations
- [ ] Plugins
- [ ] UI (React)
- [x] Docs

## How to test

```sh
cd examples/webhooks
go test ./...
```

To run the receiver against a live Bifrost instance:

```sh
WEBHOOK_SECRET=whsec_your_secret_here go run .
```

The receiver listens on `:8080` by default (override with `ADDR`). Point a Bifrost webhook endpoint at `http://<host>:8080/webhook` and complete an async job to see verified deliveries logged.

**Environment variables:**

| Variable         | Required | Default | Description                                      |
|------------------|----------|---------|--------------------------------------------------|
| `WEBHOOK_SECRET` | Yes      | —       | Endpoint signing secret (`whsec_...`)            |
| `ADDR`           | No       | `:8080` | Address the receiver listens on                  |

## Screenshots/Recordings

N/A

## Breaking changes

- [x] No

## Related issues

N/A

## Security considerations

- Signatures are compared using `crypto/subtle.ConstantTimeCompare` to prevent timing-based secret leakage.
- The timestamp tolerance window (5 minutes) blunts replay attacks.
- The request body is capped at 1 MiB (`io.LimitReader`) to prevent memory exhaustion from malicious senders.
- Verification failure reasons are logged server-side but not echoed in the HTTP response, avoiding attacker probing.
- The `whsec_` signing secret is read from an environment variable; the README explicitly warns never to hard-code it.

## Checklist

- [x] I read `docs/contributing/README.md` and followed the guidelines
- [x] I added/updated tests where appropriate
- [x] I updated documentation where needed
- [x] I verified builds succeed (Go and UI)
- [ ] I verified the CI pipeline passes locally if applicable
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.

2 participants