Cache invalidation

Cache invalidation

Wiring the webhook

  1. Put a random value of at least 32 characters in PRESS_SECRET where your app runs.
  2. In barakoBrew, create a workflow for each content type the site renders, event Published. A workflow matches one content type, so a site whose posts are an article type needs it on article, and one on post never fires.
  3. Add a Webhook action to each with Url set to https://your-site/api/revalidate and Secret set to the same value. On a site with sites configured, the Secret is the tenant's key instead, below.

One key per tenant

With sites configured, one app serves many tenants, and each tenant's admin can read and edit its own workflows. So no tenant is given PRESS_SECRET. Each delivery is verified with a key derived for the tenant its host resolves to:

key = lowercase hex HMAC-SHA256(PRESS_SECRET, "revalidate." + tenant handle)

A delivery signed with one tenant's key gets a 401 on every other tenant's host, and so does one signed with PRESS_SECRET itself. Print a tenant's key where PRESS_SECRET (or REVALIDATE_SECRET) is set:

npx barakopress revalidate-key baryo
# or, with no Node:
printf 'revalidate.%s' baryo | openssl dgst -sha256 -hmac "${PRESS_SECRET:-$REVALIDATE_SECRET}" | sed 's/^.* //'

Both read the secret from the environment and print only the key. Paste it into the Secret of that tenant's Webhook action, with Url on that tenant's domain. A new tenant needs nothing on the renderer. Changing PRESS_SECRET changes every tenant's key, so each workflow needs its new one.

A site without sites (one build, one site) verifies with PRESS_SECRET itself, as it did with REVALIDATE_SECRET. Its workflow holds that value, so give it a value no other deployment uses.

Upgrading a multi-tenant site to 0.4.0 is a breaking change. Every tenant's workflow holds REVALIDATE_SECRET today, and after the upgrade that value verifies for no tenant: deliveries get a 401 and purges stop, with the backstop the only refresh. Before or right after deploying, print each tenant's key and put it in that tenant's Webhook Secret. Then set a new PRESS_SECRET and print the keys again, because every tenant admin has seen the old value and could derive any tenant's key from it.

The barakoCMS webhook body does not name its tenant yet, so the host is what picks the key. Once the body carries the tenant (barakoCMS #868), a delivery whose body names a different tenant than its host resolves to is to be refused.

If your site sets trailingSlash: true, the webhook URL needs the slash. Next redirects /api/revalidate to /api/revalidate/ with a 308, and barakoCMS does not follow redirects on a webhook, deliberately: following one was an SSRF hole, because the guard checked only the first hop. So a URL without the slash gets a 308, the delivery is recorded as failed, it retries five times and gives up, and the cache is never purged. The site keeps working because of the backstop, so the only symptom is that publishing feels slow.

barakoCMS refuses to sign a delivery to an http:// URL unless Webhooks:AllowInsecureSignedUrls is on, which is for a loopback receiver in a lab. In production the URL is https, so this is not in your way.

Verification follows the recipe in the API's docs/webhooks.md: HMAC-SHA256 over "<timestamp>.<raw body>", compared in constant time, with a 300 second tolerance so a captured delivery cannot be replayed later, and each signature honoured once per tenant. An unsigned, missigned or stale delivery gets a 401. If neither PRESS_SECRET nor REVALIDATE_SECRET is set, or PRESS_SECRET is shorter than 32 characters, the endpoint answers 503 and purges nothing, because an open cache-purge endpoint is a free denial of service.