Skip to main content
A change that needs approval is only as useful as the reviewer knowing about it. Warden delivers pause and resolve events to any HTTPS endpoint you nominate.
warden.yaml
Nothing waits on delivery. Notifications are a subscriber to the same event stream the operator view uses, not a step in the pipeline. An endpoint that is slow, down, or hostile delays no approval and blocks no agent — a reviewer who never got the message can still open the operator view, which remains the authority.

What is delivered

A resolved event carries the same operation plus how it ended:
Two things about the payload are deliberate:
  • The statement keeps its parameters unbound ($1), exactly as the operator view shows it. You see the shape of the operation, not the customer data it touches.
  • The preview is already masked and column-restricted, by the gate, before it ever reaches the pending queue. A notification cannot leak a value the agent itself was not allowed to read.
Approve and reject links still require an operator token — the URL is a destination, not an authorisation.

Verifying a delivery

Every request carries X-Warden-Event, X-Warden-Delivery, and:
v1 is HMAC-SHA256(secret, "<t>.<raw body>"), hex-encoded. Verify it before acting on anything, and reject deliveries whose timestamp is outside a few minutes — that is what stops an old, valid message being replayed at you.
Sign over the raw body, before any JSON parsing and re-serialising, or the bytes will not match.

Delivery behaviour

Retries are deliberately few. A notification is a convenience, so a queue that grows while an endpoint is down would cost more than the messages are worth. Watch warden_webhook_deliveries_total by result:
Anything above zero means reviewers are not being told, which is the only failure mode this feature really has.

Requirements

string
Where reviewers reach this gateway. Used to build the links in a payload; omitted entirely if unset, rather than guessing an address that may not resolve from wherever the message is read.
string
required
Must be https. A plaintext http endpoint is refused unless allow_insecure: true is set, and warns at startup every time: the payload carries the statement and the preview, and a signature proves origin without hiding content.
string
required
At least 16 characters. Required, because an unsigned webhook is one anyone who learns the URL can forge — and these carry approval requests.
array
proposed, resolved, or both. Empty sends both.
Slack is next, and builds on this. Approve and reject buttons in a Slack message, verified with Slack’s signing secret, with each Slack user mapped to a named operator so the audit chain records who decided. Until then, a small relay that receives these webhooks and posts to Slack gets you most of the way — the payload already carries the diff.