Webhooks
Operator webhook receiver — GitHub push events
Webhooks
The operator runs an HTTP webhook receiver alongside the controller. GitHub pushes patch a Sync operation onto matching Apps — the generation bump reconciles immediately, no waiting for the 60s resync.
Receiver Endpoints
| Endpoint | Provider | Status |
|---|---|---|
POST /webhook/github | GitHub | ✅ Works — no signature validation |
POST /webhook/gitlab | GitLab | � Accepts 200 but parses nothing |
Bind: --webhook-bind-address, default :8082.
GitHub Push Flow
GitHub push
│
▼
POST /webhook/github
│
├─ 1. Parse JSON body (repository.clone_url, ref, after, pusher)
├─ 2. List all Apps (every namespace on the mgmt cluster)
├─ 3. Match: source.git.repoURL == clone_url
│ AND source.git.branch == pushed ref
│ AND spec.webhook.enabled == true
└─ 4. Merge-patch spec.operation on each match:
{kind: Sync, initiatedAt: now, triggeredBy: "webhook:github"}
│
▼
AppReconciler fires immediately (generation bump)Response: {"matched": N} — no apps list in the body.
Match Rules
| Check | Detail |
|---|---|
| Repo | repository.clone_url must equal source.git.repoURL exactly — .git suffix and scheme both matter |
| Branch | ref (refs/heads/main) must equal source.git.branch |
webhook.enabled | Required — apps without it never match |
source.type | Git only — image apps aren't matched |
Signature Validation — 📋 planned
Not implemented. spec.webhook.secretRef exists in the schema but the handler never reads it — X-Hub-Signature-256 isn't verified. Anyone who can reach :8082 can trigger syncs on matching apps. Put the receiver behind a private ingress or network policy until validation lands. Rate limiting is also 📋 (in-handler TODO).
The intended flow once wired:
- Load the secret named by
webhook.secretRef - HMAC-SHA256 the body, compare to
X-Hub-Signature-256 401on mismatch
Exposing the Receiver
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: wardn-webhook
annotations:
cert-manager.io/cluster-issuer: letsencrypt
spec:
tls:
- hosts: [webhook.wardn.example.com]
rules:
- host: webhook.wardn.example.com
http:
paths:
- path: /webhook/github
pathType: Prefix
backend:
service:
name: wardn-operator
port:
number: 8082Testing
curl -X POST http://localhost:8082/webhook/github \
-H "Content-Type: application/json" \
-d '{
"repository": { "clone_url": "https://github.com/myorg/my-app.git" },
"ref": "refs/heads/main",
"after": "abc123"
}'
# {"matched":1}Then confirm the op landed:
kubectl get app my-app -n org-my-org -o jsonpath='{.spec.operation.triggeredBy}'
# webhook:githubGitLab
POST /webhook/gitlab returns 200 without acting — full payload parsing is planned.
Related
- Webhook Triggers Guide — user-facing setup
- Configuration —
webhookspec fields - Reconciliation Loop — what happens after the op lands