# Flux CD Webhooks

## When to Use This Skill

* How do I receive Flux CD webhooks (notification-controller `generic-hmac` Provider)?
* How do I verify the Flux CD `X-Signature` header?
* Why does my Flux webhook signature verification fail?
* How do I handle Flux events (`severity` `info` / `error`, `reason` like `ValidationFailed`)?

## Setup note

Use a Flux Provider of type generic-hmac and paste the token from its Secret. Generic providers are unsigned and fail verification.

## Verification (core)

Flux signs the **raw request body only** with HMAC-SHA256, keyed with the `token` from the Provider's Secret (UTF-8 bytes, used as-is). The `X-Signature` header is `sha256=<hex>`; accept the bare hex digest too. No timestamp or id is signed, so there is no replay window to check. Validate the digest format (64 hex characters) before decoding, then compare in constant time. Missing, malformed or mismatched signature: `401`.

Node:

```javascript
const crypto = require('crypto');

function verify(rawBody /* Buffer */, header, secret) {
  if (!header || !secret) return false;
  const digest = header.startsWith('sha256=') ? header.slice(7) : header;
  if (!/^[0-9a-fA-F]{64}$/.test(digest)) return false;
  const expected = crypto.createHmac('sha256', secret).update(rawBody).digest();
  return crypto.timingSafeEqual(Buffer.from(digest, 'hex'), expected);
}

```

Python:

```python
import hmac, hashlib, re

def verify(raw_body: bytes, header: str | None, secret: str) -> bool:
    if not header or not secret:
        return False
    digest = header.removeprefix("sha256=")
    if not re.fullmatch(r"[0-9a-fA-F]{64}", digest):
        return False
    expected = hmac.new(secret.encode(), raw_body, hashlib.sha256).digest()
    return hmac.compare_digest(bytes.fromhex(digest), expected)

```

Read the raw body as bytes (`express.raw({ type: '*/*' })`, `await request.arrayBuffer()`, `await request.body()`) and parse JSON only after the check.

> **For complete handlers with tests**, see [examples/express/](https://github.com/hookdeck/webhook-skills/tree/main/skills/flux-cd-webhooks/examples/express/), [examples/nextjs/](https://github.com/hookdeck/webhook-skills/tree/main/skills/flux-cd-webhooks/examples/nextjs/), [examples/fastapi/](https://github.com/hookdeck/webhook-skills/tree/main/skills/flux-cd-webhooks/examples/fastapi/).

## Event Types

Flux has no event-name field or header. Every delivery is one Flux Event; branch on `severity` and `reason`.

| `severity` | Meaning | Example `reason` |
| --- | --- | --- |
| `error` | A reconciliation or validation failed | `ValidationFailed` |
| `info` | Normal progress | `ReconciliationSucceeded` |

Other body fields: `involvedObject` (`kind`, `name`, `namespace`, `uid`), `message`, `metadata`, `reportingController`, `reportingInstance`, `timestamp`. Which events arrive is set by the `Alert` (`eventSeverity`, `eventSources`).

> **For the Event object**, see [Flux notification Provider docs](https://fluxcd.io/flux/components/notification/providers/)

## Important Headers

| Header | Description |
| --- | --- |
| `X-Signature` | `sha256=<hex>` HMAC-SHA256 of the raw body (`generic-hmac` Providers only) |
| `Gotk-Component` | Controller that raised the event, e.g. `kustomize-controller` |
| `Content-Type` | `application/json` |

Requests are `POST`. Hookdeck verifies `sha256` only; Flux notes the default hash "may change in the future", so a delivery signed with another hash fails.

## Environment Variables

```bash
FLUX_CD_WEBHOOK_SECRET=your_provider_secret_token   # `token` key of the Secret in Provider spec.secretRef

```

## Local Development

```bash
# Start tunnel (no account needed)
npx hookdeck-cli listen 3000 flux-cd --path /webhooks/flux-cd

```

## Reference Materials

* [references/overview.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/flux-cd-webhooks/references/overview.md) - Flux notification concepts, Event shape
* [references/setup.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/flux-cd-webhooks/references/setup.md) - Provider, Secret and Alert configuration
* [references/verification.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/flux-cd-webhooks/references/verification.md) - Signature verification details