-
Notifications
You must be signed in to change notification settings - Fork 0
Events and webhooks
warden history answers afterwards, and only if somebody thinks to look. The
interesting moments are the ones nobody is watching for: a port taken while a
deploy runs, a lease that quietly expired overnight, a holder that died and left
its port booked.
Every one of those already lands in warden's own book. This carries it further.
$ warden events
09:41:02 registered shop-api 127.0.0.1:8600
09:41:11 renewed shop-api 127.0.0.1:8600
09:41:44 released shop-api 127.0.0.1:8600warden events --json writes one event per line and flushes each as it arrives,
so it pipes into anything:
warden events --json | jq -r 'select(.action == "expired") | .name'GET /v1/events is the same stream as server-sent events, behind the same token
as every other read. See HTTP API.
There are five actions:
| Action | When |
|---|---|
registered |
A name got a port it did not have |
renewed |
A heartbeat, or a re-register on the same port |
moved |
A name came back on a different port |
released |
Given back, by the holder or by warden release
|
expired |
A lease ran out and warden took the port back |
warden setup asks for all of this and posts a test event before saving
anything. One setting at a time works too:
warden settings set webhook https://discord.com/api/webhooks/...
warden settings set webhook_format discord
warden settings set webhook_events registered,released
warden webhook --testRestart the warden afterwards. It reads its settings once, at startup.
| Discord | Channel → Edit → Integrations → Webhooks → New Webhook → copy the URL |
| Slack | api.slack.com/apps → your app → Incoming Webhooks → Add New Webhook |
| Teams | Channel → ⋯ → Workflows → "Post to a channel when a webhook request is received" |
| Your own | Anything that accepts a POST |
The Teams route is a Power Automate flow because the old Office 365 connector cards are being retired; warden posts an adaptive card in a message, which is what that flow accepts.
webhook_format |
What arrives |
|---|---|
json |
The event as it is, signed. Build anything custom on this |
discord |
An embed, coloured by action |
slack |
Blocks, plus a text fallback for phone notifications |
teams |
An adaptive card in a message attachment |
With json and a webhook_secret, every post carries a signature:
X-Warden-Signature: sha256=b1646dcf...
That is an HMAC over exactly the bytes that were sent, keyed with the secret. warden serialises the body itself rather than leaving it to the HTTP client, because a signature over a body somebody else re-serialises signs something else.
expected = hmac.new(secret.encode(), request.body, hashlib.sha256).hexdigest()
hmac.compare_digest(request.headers["X-Warden-Signature"], f"sha256={expected}")Every post also carries X-Warden-Node and X-Warden-Event, whatever the shape.
Handing out a port never waits on a webhook. Delivery happens after the change is committed and off the request path, retried three times and then given up on. A chat server having a bad afternoon cannot make a port take longer to hand out. A reader that falls behind loses its oldest event, never its newest, and never the write.
A webhook that fails is not allowed to fail quietly. From the inside, one
failing all day looks exactly like a quiet day. warden webhook and
warden doctor both report the target, the shape and the last failure:
$ warden doctor
...
warn events to https://discord.com/... are not arriving - Server error '502 Bad Gateway'The address is always cut back to its host. A webhook URL is a credential, and its path is the half worth stealing.
Every warden reports its own events, so every node you want to hear from needs the setting. A hub does not relay its nodes' events — a node that cannot reach the hub still hands out ports, and the same independence applies here.
warden — nothing binds a port without asking ·
uv tool install warden-ports
Repository · Issues · Releases · PyPI · MIT
Getting started
While it runs
Several machines
Reference