Expand description
Operator notifications for ACME lifecycle events.
The shape mirrors filter/signer: a
trait, an error type, and a from_config selector building the
configured set at startup. Three backends exist today —
email, webhook (any HTTP endpoint, with the URL, method, headers
and body all configured, which is what makes a chat provider configuration
rather than code), and custom (an external script, for a channel that
is not an HTTP request at all).
§Fire-and-forget, always — and now durable
A notification can never affect the ACME response that triggered it.
NotifyDispatcher::dispatch returns () and cannot be ?’d: it writes
the delivery to the durable queue and returns. That is a
deliberate API shape, not a convention callers must remember — there is no
method on this type whose result a handler could propagate.
What changed is what happens after it returns. Delivery used to be a bare
tokio::spawn, so a refused SMTP connection or a 503 from a webhook was
logged once and the notification was gone, and a restart lost everything in
flight — the operator never heard that a certificate had been issued, and
nothing recorded that they hadn’t. Now each delivery is a notify_deliver
job row: a transport failure is retried under jobs.max_attempts and the
shared backoff, and a row outlives the process that queued it.
Two consequences worth not rediscovering:
- One job per (occurrence × backend), never one per event. A retry must not re-send to a backend that already succeeded, or one flaky webhook produces a duplicate email on every attempt.
NotifyErrorcarries whether it is worth retrying. A template that does not render and a 400 from a webhook will fail identically for ever, so they are permanent and refused on the first attempt; a connection refused, a timeout and a 503 have decided nothing, so they are retried. That iscrate::jobs’sRetry/Failedsplit, applied at the source.
§Per-backend event filtering
Unlike FilterConfig::rules, where every
filter must agree, notify backends are independent broadcast side-channels
— an operator plausibly wants email only for issuance/revocation and a chat
webhook for everything including failures. Each backend’s own events
list (defaulting to all six kinds) decides what reaches it, and the list
lives on its BackendSlot so the check happens once, in
NotifyDispatcher::dispatch, rather than in each backend’s own delivery
code. Filtering there rather than in a wrapper around send is what stops
a job being queued for a delivery that would immediately do nothing.
§Per-profile, like every other subsystem
build_registry builds one NotifyDispatcher per resolved profile
(not deduplicated by configuration identity like signer backends are —
dispatchers are stateless side-channels, so two profiles with identical
[notify] sections simply get two independent instances). The
asynchronous relay signer backend, whose completion happens outside
any HTTP handler, is handed the whole profile name -> dispatcher map so
it can notify the right profile once an order settles — see
signer::relay::flow::settle.
Re-exports§
pub use job::NOTIFY_JOB_KIND;pub use job::NotifyJob;
Modules§
- custom
- The
customnotify backend: shells out to an external script or webhook wrapper, on the same contract ascrate::filter::customandcrate::signer::custom— env vars plus JSON on stdin, exit code decides outcome. This is what lets an operator wire up a channel this server has no built-in support for (Slack, PagerDuty, …) without a code change. - The
emailnotify backend: SMTP vialettre. - job
- One queued notification delivery, as a durable job.
- webhook
- The
webhooknotify backend: one HTTP request per event, with the URL, the method, the headers and the body all stated by the operator.
Structs§
- Account
Created Data - Payload of
NotifyEvent::AccountCreated: a client registered a new account at this endpoint. - Account
Deactivated Data - Payload of
NotifyEvent::AccountDeactivated: an account was deactivated, either by the client (§7.3.6) or byacme-proxy account deactivate. - Backend
Slot - One configured backend, under the id a queued job addresses it by.
- Certificate
Issued Data - Payload of
NotifyEvent::CertificateIssued: a certificate was signed. - Certificate
Revoked Data - Payload of
NotifyEvent::CertificateRevoked: a certificate was withdrawn. - Challenge
Failed Data - Payload of
NotifyEvent::ChallengeFailed: a validation attempt did not succeed. - Notifiers
- The
profile name -> dispatchermap, as a handle that survives a configuration reload. - Notify
Dispatcher - The configured notify backends for one profile.
- Notify
Error - Why a notify backend failed to deliver, and whether asking again could help.
- Profile
Mounted Data - Context common to every event: the profile it happened on and the client address, when a request was in scope.
Enums§
- Notify
Event - One lifecycle event, carrying everything a template or
customscript needs to describe it.
Traits§
- Notify
Backend - A pluggable notification channel.
Functions§
- build_
registry - Builds one
NotifyDispatcherper resolved profile, keyed by profile name — the map therelaysigner backend needs to notify the right profile from its background completion task, where there is noAppState/Profileto reach through. - from_
config - Builds the configured notify dispatcher. Called once per profile at
startup (via
build_registry), so it may fail fast. - notifiers_
channel - One cell the reload path replaces, and every reader sees the new map at the
next
get.
Type Aliases§
- Dispatcher
Map - One generation’s
profile name -> dispatchermap. - Notifiers
Sender - The writing half of a
Notifiershandle.