Contents

Roadmap: Git webhook → automatic Hugo rebuild

Status: 🗂️ BACKLOG — not yet implemented

This page documents an architectural intent to be implemented in a future iteration. The code is not yet in production.

Context

In Strategy 4 (separating MCP / Git), I explained why content/ is in .gitignore on the arleo.eu repo: so that no conflict is possible between MCP writes and Git writes.

Concretely, this means that when I push a new version of layouts/, themes/, static/, hugo.toml, or deploy.sh from VS Code, nothing happens automatically server-side. I have to SSH into the Hugo VM and manually run git pull && hugo --minify && rsync.

Not critical (structure pushes happen ~1× per week), but it’s unnecessary friction. So: GitHub webhook → auto-rebuild.

Target architecture

Diagram Diagram
GitHub push
    │
    │ POST https://mcp-hugo.arleo.eu/webhook/git
    │ Header: X-Hub-Signature-256: sha256=<hmac>
    │ Body: {"ref": "refs/heads/main", "head_commit": ...}
    │
    ▼
[Cloudflare]
    │ Custom rule: skip Bot Mgmt if IP in GitHub ranges
    │
    ▼
[mcp-oauth-proxy] (NUC, port 443)
    │ Routing /webhook/git → forward to MCP server VM
    │
    ▼
[hugo-mcp] (Hugo VM, port 8000)
    │ 1. Validate HMAC-SHA256
    │ 2. Validate refs/heads/main only
    │ 3. Rate limit: 1 rebuild / minute
    │ 4. Trigger: git fetch && git reset --hard origin/main && hugo --minify
    │ 5. JSON audit log: commit SHA, author, duration
    │
    ▼
[Site rebuilt] ~3 seconds

Technical choices

HMAC-SHA256 (not just IP whitelist)

GitHub publishes its ranges (140.82.112.0/20 and others). We could just filter by IP. But HMAC is more robust:

  • IP spoofing is theoretically possible (very rare, but)
  • HMAC signature relies on a shared secret. If the attacker doesn’t have it, they can’t forge anything.

Concretely we do both: Cloudflare does IP filtering (eliminates 99% of noise), and HMAC validates cryptographically.

Rate limiting: 1 / minute

Why? If an attacker manages to sign a payload (e.g. via WEBHOOK_SECRET leak), they could spam the server with rebuilds. Hugo build = ~3s + Cloudflare API call = ~400ms. At 100 rebuilds/minute, the server saturates and the Cloudflare API complains.

1 rebuild/minute amply covers the legitimate case (I don’t push more than that) and limits the blast radius in case of compromise.

refs/heads/main validation only

If someone pushes a feature branch, we do NOT rebuild. Only main triggers deployment. Avoids deploying work-in-progress.

JSON audit log

Each rebuild trigger logs:

{
  "ts": "2026-05-09T14:23:11Z",
  "event": "webhook_rebuild_triggered",
  "commit_sha": "a1b2c3d4...",
  "commit_author": "jmrGrav <276982731+jmrGrav@users.noreply.github.com>",
  "commit_message": "fix: typo in footer",
  "build_duration_ms": 2853,
  "deploy_success": true,
  "cf_purge_success": true
}

Ingested by Vector → BetterStack for timeline + alerting.

Layer-by-layer security

LayerProtection
CloudflareGitHub IP whitelist (140.82.112.0/20)
Cloudflare“Skip Bot Mgmt on webhook path” triple-scoped rule
nginxTLS 1.3 mandatory
systemdIPAddressAllow GitHub ranges (rebuild git fetch)
MCPHMAC-SHA256 validation (32-byte random secret)
MCPStrict refs/heads/main validation
MCPRate limiting 1/min (slowapi)
MCPJSON audit log ingested into BetterStack

Defense-in-depth. If one layer is compromised, others hold.

What already works

  • Strategy 4 (.gitignore content/) in place repo-side
  • mcp-oauth-proxy can already route to new endpoints without OAuth redeployment
  • Cloudflare Bot Management scoped bypass already created (cf. CF blocking post-mortem)
  • Structured JSON audit log not yet in place MCP-side — that’s a security sprint prerequisite

What’s left to do

  1. Implement /webhook/git endpoint in hugo-mcp (~2h)
  2. Configure GitHub webhook with HMAC secret (5 min)
  3. Reproducible tests: real push → auto rebuild in less than 10s (~30 min)
  4. Rollback procedure: if rebuild breaks the site, auto-revert to last valid commit (~1h)

Total estimate: ~4h. Reasonable effort, but not priority vs the ongoing MCP security sprint.

Decision

Webhook Git Strategy 4 will be implemented after the MCP security sprint (rate limiting + JSON audit logs + Pydantic v2). Several of this webhook’s security layers (rate limiting, audit logs) are sprint chantiers — no point re-implementing them here, they’ll be globally available after.

ETA: June 2026.

Reference

Full technical brief archived in the hugo-mcp repo: docs/backlogs/webhook-git-rebuild-2026-05-08.md. Includes detailed Python code, nginx snippets, exact Cloudflare config, and rollback runbook.