Webhooks
When [webhook] enabled = true, the registry POSTs HMAC-signed JSON events to a
configured receiver. Delivery is best-effort and non-blocking — events go to
a bounded in-memory queue and are delivered by a background worker; the HTTP
request that triggered an event never waits on (or fails because of) webhook
delivery. Implementation: crates/acdp-registry-webhook/src/lib.rs; event types:
crates/acdp-registry-types/src/event.rs.
Events
Three event types. Each is delivered as a flattened envelope: the variant fields
plus event_id, schema_version, and a type discriminator.
context.published
{
"event_id": "e0f1...",
"schema_version": "1.0",
"type": "context_published",
"registry_authority": "registry.example.com",
"registry_base_url": "https://registry.example.com",
"ctx_id": "registry.example.com/ctx_...",
"lineage_id": "lin_...",
"agent_id": "did:web:agents.example.com:my-agent",
"context_type": "observation",
"visibility": "public",
"version": 1,
"created_at": "2026-06-10T12:00:00Z",
"derived_from": ["registry.example.com/ctx_parent"],
"run_id": "run-123",
"key_fingerprint": "sha256:139e…",
"registry_receipt": { "registry_did": "did:web:registry.example.com", "…": "…" }
}registry_base_url lets a federation control plane bootstrap proxy routes;
run_id echoes the publisher's X-Run-Id header (omitted if absent).
key_fingerprint and registry_receipt (ACDP 0.2.0, additive — both omitted
on a receipt-less registry) carry the RFC-ACDP-0010 §6 fingerprint of the
producer key the registry actually resolved at publish time, and the full
signed receipt, so the control plane can correlate and re-verify without
re-fetching the context.
context.retrieved
{
"type": "context_retrieved",
"registry_authority": "registry.example.com",
"ctx_id": "registry.example.com/ctx_...",
"requester_did": "did:web:agents.example.com:reader",
"at": "2026-06-10T12:01:00Z"
}requester_did is null for anonymous reads.
search.executed
{
"type": "search_executed",
"registry_authority": "registry.example.com",
"query": "weather",
"result_count": 12,
"requester_did": null,
"at": "2026-06-10T12:02:00Z"
}Signature scheme
Matches GitHub's exactly — receivers that already verify GitHub webhooks reuse the same code. Headers on every delivery:
Content-Type: application/json
X-ACDP-Signature: sha256=<hex of HMAC-SHA256(webhook.secret, raw_json_body)>
X-ACDP-Event: context.published | context.retrieved | search.executed
X-ACDP-Event-Id: <uuid, stable across retries>
X-Tenant-Id: <tenant, when the event is tenant-scoped>Verify by recomputing the HMAC over the raw request body bytes (not a re-serialization) and comparing in constant time. Reject on mismatch.
The signing input is the exact bytes posted. Don't parse-and-reserialize before verifying — key ordering or whitespace differences will break the MAC.
Delivery and retries
- Events are queued on a bounded mpsc channel of
webhook.queue_capacity(default 1024). When the queue is full the event is dropped with a warn log — back-pressure never blocks the request path. - The worker retries on
429,5xx, and transport errors with exponential backoff starting at 250 ms, doubling, capped at 15 s, up towebhook.max_retries(default 3). - A non-429
4xxis treated as a permanent failure — the worker gives up immediately (the receiver rejected the payload; retrying won't help). 2xxis success. After exhausting retries the failure is logged and dropped (fire-and-forget).
Queue depth is exposed operationally at GET /admin/status
(webhook.queue_in_flight / queue_capacity) — see
HTTP-API.md.
SSRF protection
webhook.url is validated at startup against the same SsrfPolicy as DID and
cross-registry resolution: HTTPS only, no private/internal authorities, no
redirects to such (the policy is documented in
acdp-rs · Security Model).
A webhook config that fails validation aborts startup rather than silently
disabling delivery.
Configuration
[webhook]
enabled = true
url = "https://example.com/hooks/acdp"
secret = "<random shared secret, non-empty>"
timeout_seconds = 5
max_retries = 3
queue_capacity = 1024See CONFIGURATION.md for the field reference.