Unreleased documentation. These pages follow the main branch and can change before the next release. For supported guidance, use v0.26.1.
You operate an active registry, or are about to activate one as deploy a registry
describes, whose project declares events that name a webhook destination, and a receiver is
waiting for them. At the end of this page every destination identifier the package names has a
binding in the runtime file, the receiver holds the same signing key and has been built against a
sample request, and you can list and replay deliveries.
An event is a declaration in the project, a trigger on an entity with a projection of its fields, that the runtime delivers after the write commits. A destination is the receiver the declaration names by identifier; the package carries only that identifier, and the runtime file binds it to an origin, so a module stays deployment-neutral. Send registry events to a webhook walks a first delivery on a local registry, and the events section of author a registry project covers the declaration.
Bind each destination
Section titled “Bind each destination”The package refuses to activate until the runtime file binds every destination identifier it names, and it refuses extra bindings. Destination URLs and secret references belong in the runtime file, never in a module:
eventDestinations: record-receiver: origin: https://receiver.example.org path: /events networkProfile: productionHttps dnsFamily: dualStackStrict allowedPrivateCidrs: [] hmacSha256KeyRef: secret:file/record-webhook-key classificationCeiling: restricted deliveryCeilings: attemptTimeoutMilliseconds: 1000 maximumAttempts: 3eventDelivery: payloadRetentionDays: 7The destination’s classificationCeiling must cover the event’s derived classification; a binding
cannot add fields to a payload. For a receiver on a private network, list the intended canonical
ranges in allowedPrivateCidrs. Use dnsFamily: ipv4Only only on an IPv4-only network. Optional
tls.caBundleRef and tls.clientIdentityRef name PEM secrets for a private certificate authority
and mutual TLS. networkProfile: loopbackDevelopmentHttp exists for local development only.
A binding may tighten the compiled ceilings and never widen them; the example tightens both.
bregctl explain events <project> reports the compiled values per delivery: five attempts with a
five-second timeout each, retried after 1, 2, 4, and 8 seconds, with a dead letter required when
the attempts run out. Automatic retries keep the event id and idempotency key. Any 2xx
acknowledges delivery; redirects, timeouts, and other responses follow the bounded retry policy.
Activation refuses a rebinding that would leave retained pending or dead-lettered deliveries
without the binding they were captured under. bregctl doctor opens every binding before the
process starts and reports one it cannot open as startup.event_destinations.refused, so run it
after every change to this block.
Provision the shared key
Section titled “Provision the shared key”Generate the HMAC-SHA-256 key as an owner-only file inside the secret root, as the user that runs
breg, and provision the identical bytes to the receiver through its own secret store:
(umask 077; openssl rand -hex 32 | tr -d '\n' > /etc/breg/secrets/record-webhook-key)The runtime signs with the file’s bytes exactly as written, neither trimmed nor decoded, so the
receiver must load the same 64 characters as its key rather than decode them. The key needs at
least 32 bytes, and the file must be a regular file with mode 0400 or 0600 owned by the running
user; doctor and startup refuse a binding whose key breaks either rule.
Build the receiver against a sample
Section titled “Build the receiver against a sample”Your receiver verifies X-Registry-Signature against the exact request bytes and checks
X-Registry-Delivery-Time for freshness before acting. webhook sample prints one request for
an event with placeholder identifiers and a placeholder signature, without a runtime or a
database, so the receiver can be built before the registry serves:
bregctl webhook sample ./my-registry --event record-created-v1webhook sample succeededevent: record-created-v1POST <configured-webhook-request-target> HTTP/1.1Accept: application/jsonContent-Type: application/jsonIdempotency-Key: sha256:0000000000000000000000000000000000000000000000000000000000000000X-Registry-Delivery-Attempt: 1X-Registry-Delivery-Time: 2026-01-01T00:00:00ZX-Registry-Event-Generation: 1X-Registry-Signature: v1=<computed-at-delivery>ce-dataschema: urn:breg:event-schema:generic-registry:record:record-created-v1:sha256:411f35fcbaa498497ac172fdb725d2f583d761a73d903c1647b34dc4af703855ce-id: 00000000-0000-4000-8000-000000000001ce-source: urn:registrystack:registry:generic-registry:instance:<configured-instance>ce-specversion: 1.0ce-time: 2026-01-01T00:00:00Zce-type: record-created-v1
{"entity":"record","packageRevision":"sha256:0000000000000000000000000000000000000000000000000000000000000000","recordId":"00000000-0000-4000-8000-000000000002","revision":1,"trigger":"created","values":{"code":"x","status":"draft"}}That sample carries generic-registry, the registry id bregctl init writes into registry.yaml;
your project carries its own id, schema hash, and projected fields. --format json returns the
same request as a document with the method, request target, headers, body, and canonical body. The
API reference defines the request shape and the signing input byte
order. Deduplicate retries by Idempotency-Key, use the record revision to order events, and
acknowledge only after durably recording acceptance.
Inspect and replay deliveries
Section titled “Inspect and replay deliveries”Inspect pending, dead-lettered, and expired deliveries, then replay a dead letter after fixing the receiver:
bregctl --format json webhook list \ --runtime-config /etc/breg/runtime.yaml --limit 50
bregctl webhook replay \ --runtime-config /etc/breg/runtime.yaml \ --event-id "<eventId>" --delivery-id "<deliveryId>" \ --expected-generation "<generation>"The list carries delivery metadata and no record values, and --limit accepts 1 to 100. A
delivery that exhausts its attempts becomes a dead letter with replayEligible: true while its
payload is retained. Successful delivery erases the payload, and retained payloads expire after
payloadRetentionDays, which defaults to 7 and is capped at 30. An empty list is not proof of
success: in-flight attempts and completed deliveries are excluded, so confirm effects from the
receiver’s own record. Replay keeps the event id and issues a new generation and idempotency key;
if the receiver’s earlier effect is uncertain, check its state before replaying. A replay whose
--expected-generation no longer matches, whose payload has expired, or whose destination is no
longer bound is refused with webhook.operation.refused, and the list shows which.
Operator commands connect to PostgreSQL directly. When the database uses a private certificate
authority, set SSL_CERT_FILE to its PEM bundle for each command.
Troubleshooting
Section titled “Troubleshooting”| Symptom | Next move |
|---|---|
| Activation refuses the package over destinations | Every destination identifier the package names needs one binding, and no binding may name an identifier the package does not. Compare the eventDestinations keys with bregctl explain events. |
doctor reports startup.event_destinations.refused | Check the key file’s owner, mode, and length, the origin against the network profile, and allowedPrivateCidrs for a private receiver. |
| The receiver rejects every signature | The runtime signs the raw file bytes. Load the same characters on the receiver without decoding them, and verify against the exact request bytes in the documented signing-input order. |
| A webhook never fires | Confirm the destination is bound, the receiver returns 2xx, and the event’s condition matched; use webhook list for dead letters. |
| Replay is refused | Refresh the list and check the generation, replayEligible, payload expiry, and the destination binding. |
- Send registry events to a webhook to see one delivery end to end on a local registry.
- Author a registry project for the event declaration, its projection, and its classification.
- Base Registry Engine API reference for the delivery request shape, the signing input, and the CloudEvents headers.
- Base Registry Engine configuration reference for every
eventDestinationsandeventDeliverykey.