Skip to main content

Webhooks

Struct Webhooks 

Source
pub struct Webhooks { /* private fields */ }
Expand description

A module that delivers signed outbound webhooks.

Implementations§

Source§

impl Webhooks

Source

pub fn new() -> Self

Defaults: dead-letter after five failed attempts, no signature alias.

Source

pub fn from_config(cfg: &dyn Config) -> Result<Self, ConfigError>

The module as the deployment configures it. WEBHOOKS_SIGNATURE_ALIAS names a second header that carries the same signature, for receivers that verify under their product’s own header name; unset is Webhooks::new. WEBHOOKS_MAX_ATTEMPTS is still read by the drain.

§Errors

ConfigError when the alias is not an HTTP header token, or names a header the module or the transport already sends. Build the module with this at boot to refuse a bad alias then. The drain reads the alias from its own config on every pass, so it refuses one then too.

Source

pub fn signature_headers(&self) -> Vec<&str>

The signature header names Webhooks::from_config stored: SIGNATURE_HEADER, then the alias if there is one. All of them carry the same value. The drain takes its names from the config it is given.

Source

pub fn max_attempts(self, max_attempts: u32) -> Self

Dead-letter a delivery after this many failed attempts instead of five. Values outside 1..=1_000 clamp to the nearest bound — the same range WEBHOOKS_MAX_ATTEMPTS must pass validation with. WEBHOOKS_MAX_ATTEMPTS overrides this when set.

Source

pub async fn create_endpoint( &self, db: &dyn Database, subject: &str, url: &str, event_types: &[&str], now: &str, ) -> Result<CreatedEndpoint, EndpointError>

Registers one delivery endpoint for subject and returns its signing secret — the only time the secret is ever handed back. List the endpoints later and the secret is not in the answer.

event_types is a filter; an empty slice means “everything” (*). A URL that is not http(s), or a filter naming an event type that could never ride a header, is refused here rather than failed at every delivery.

§Errors

EndpointError for a malformed URL or subject, or a filter naming an unusable event type, and DbError when the insert fails.

Source

pub async fn endpoints( &self, db: &dyn Database, subject: &str, ) -> Result<Vec<Endpoint>, DbError>

The endpoints registered for subject, creation order — without their secrets.

§Errors

DbError when the read fails.

Source

pub async fn delete_endpoint( &self, db: &dyn Database, subject: &str, endpoint_id: &str, ) -> Result<bool, DbError>

Deletes one endpoint of one subject; false when no such endpoint belongs to that subject. Events already queued for it are dropped by the next drain — there is nobody left to deliver them to.

§Errors

DbError when the delete fails.

Source

pub async fn publish( &self, db: &dyn Database, subject: &str, event_type: &str, data: &Value, now: &str, ) -> Result<Published, PublishError>

Fans one event out to every endpoint of subject whose filter matches event_type, and returns the outbox INSERTs — append Published::into_statements to the caller’s own db.batch_atomic(..), so the event is durable exactly when the state change that caused it is. Nothing is written until that batch runs.

An event no endpoint matches is Published::endpoints == 0 and nothing else — not an error: “nobody subscribed to that” is an ordinary answer.

§Errors

PublishError::InvalidEventType for an event type that could never ride the Cratefield-Event-Type header (refused before any read, so it cannot burn attempts later), and DbError when the endpoint read fails.

Source

pub async fn drain_with( &self, ctx: &ModuleContext, ) -> Result<DrainReport, DeliveryError>

Delivers due outbox rows through the ports of ctx, spending no more than what the invocation’s ctx.scheduled budget allows (ADR 0023). Call it from the venture’s scheduled entry point — a cron invocation builds no router, so the context must be the one the runtime hands in, not one parked at router-build time.

Each row is signed and delivered once per pass: 2xx completes it, anything else retries with exponential backoff (410 Gone and SSRF-blocked destinations dead-letter immediately), and MAX_ATTEMPTS failures dead-letter it for Webhooks::replay. One delivery attempt spends one subrequest; when the budget runs out the rows it never reached stay queued, due now, for the next tick. A per-row failure never aborts the pass.

The signature names come from ctx’s WEBHOOKS_SIGNATURE_ALIAS, on every pass, whichever constructor built this module.

§Errors

DeliveryError when a port is missing, the claim fails, the outbox refuses a completion write, or the alias is not usable. An unusable alias is refused before any row is claimed.

Source

pub async fn replay( &self, db: &dyn Database, subject: &str, dead_letter_id: &str, now: &str, ) -> Result<bool, DbError>

Re-enqueues one dead letter of subject — attempts reset to zero — and removes it, in one batch. The delivery log keeps the history. false — the letter left exactly where it was — when the id names no dead letter of this subject, or when the letter’s endpoint is gone: a replay into a deleted endpoint would consume the letter and then be dropped by the drain, destroying the event for good.

§Errors

DbError when the read or the batch fails.

Source

pub async fn deliveries( &self, db: &dyn Database, subject: &str, limit: u64, ) -> Result<Vec<Delivery>, DbError>

A subject’s delivery log, newest attempt first. status_code is None for an attempt that never got an answer.

§Errors

DbError when the read fails.

Source

pub async fn dead_letters( &self, db: &dyn Database, subject: &str, limit: u64, ) -> Result<Vec<DeadLetter>, DbError>

A subject’s dead letters, oldest first — the ids Webhooks::replay takes.

§Errors

DbError when the read fails.

Trait Implementations§

Source§

impl Clone for Webhooks

Source§

fn clone(&self) -> Self

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for Webhooks

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Default for Webhooks

Source§

fn default() -> Self

Returns the “default value” for a type. Read more
Source§

impl Module for Webhooks

Source§

fn personal_data(&self) -> &'static [PersonalDataSet]

Every table keys its rows on the subject the event (or endpoint) belongs to, so erasure reaches all four; the endpoint secret is credential material and is exported redacted.

Source§

fn router(&self, _ctx: ModuleContext) -> Router

Deliberately empty: see the crate documentation — the Rust API is this module’s surface, and a management route would need an auth decision this crate does not make.

Source§

fn scheduled<'a>( &'a self, ctx: &'a ModuleContext, cron: &'a str, ) -> BoxFuture<'a, Result<(), AnyError>>

The recovery half of the outbox contract: whatever a caller’s immediate drain never got to is delivered on the next tick — as far as the invocation’s budget reaches (ADR 0023), with the rest left due for the ticks after — and the dead letters pile up here for Webhooks::replay.

Source§

fn name(&self) -> &'static str

Kebab-case name; mounted at /v1/<name>.
Source§

fn version(&self) -> &'static str

env!("CARGO_PKG_VERSION"), surfaced by /__health.
Source§

fn requires(&self) -> &'static [Port]

Ports the module cannot run without; missing = build error.
Source§

fn tables(&self) -> &'static [&'static str]

Table names this module owns; duplicates across modules are a build error.
Source§

fn migrations(&self) -> Migrations

The module’s migrations, embedded per dialect.
Source§

fn validate_config(&self, cfg: &dyn Config) -> Result<(), ConfigError>

Rejects invalid configuration: missing required keys or malformed values, reported together with the module name. Read more
Source§

fn harness_api(&self) -> u32

Contract version, checked by Harness::build.
Source§

fn optional(&self) -> &'static [Port]

Ports the module uses when present.
Source§

fn depends_on(&self) -> &'static [&'static str]

Modules this one’s schema sits on top of, by name. Read more
Source§

fn actor_kinds(&self) -> &'static [&'static str]

Actor kinds this module owns, each [a-z0-9_-]; duplicates across modules are a build error, and so is a kind with no handler registered through HarnessBuilder::actor. Read more
Source§

fn emits(&self) -> &'static [&'static str]

Event names this module emits ("<module>.<event>"), listed by /__health.
Source§

fn public_writes(&self) -> bool

Whether the module has public write endpoints. Read more
Source§

fn public_write_policy(&self) -> RoutePolicy

What actually protects those public writes, for a module with no ADR 0010 surface to hang a per-action RoutePolicy on (issue #143). Read more
Source§

fn signature_verification(&self) -> SignatureVerification

Which verifier proves this module’s RoutePolicy::Signature deliveries (issue #533): how a webhook handler checks a machine signature before acting. Read more
Source§

fn max_body_bytes(&self, _cfg: &dyn Config) -> usize

The largest request body any route of this module accepts, so a runtime can refuse an oversize body before buffering it (issue #440). Read more
Source§

fn streaming_routes(&self) -> &'static [StreamRoute]

Routes this module serves in streaming mode (issue #585), each with its own StreamRoute::max_bytes ceiling. Opt-in: the default is empty, and every existing route keeps the buffered behaviour it always had. Read more
Source§

fn max_blob_object_bytes(&self) -> u64

The largest object this module’s streamed and multipart blob writes may produce (issue #586), enforced by the ScopedBlob the harness hands the module on put_stream, upload_part and complete_multipart. Read more
Source§

fn self_check(&self) -> Vec<String>

Problems the module can find in itself, with no configuration and no environment: its own embedded data, checked against its own declarations. Read more
Source§

fn well_known(&self) -> Option<Router>

Routes this module serves at the root under /.well-known, for spec-mandated discovery documents (OIDC openid-configuration, jwks.json) that must live outside /v1 (issue #46). Paths are relative to the prefix: register /jwks.json, not /.well-known/jwks.json. Read more
Source§

fn surface(&self) -> Surface

The module’s UI surface (ADR 0010): the actions a renderer may offer and the views that compose them. Input schemas come from the handler’s own body types (Action::input::<Body>()), so the declaration cannot drift from the route. Harness::build validates it; GET /__surface serves the composition. Default: nothing, and a module that declares nothing renders nothing.
Source§

fn events( &self, ) -> Vec<(String, Arc<dyn Fn(&Scope, Value) -> Pin<Box<dyn Future<Output = Result<(), Box<dyn Error + Send + Sync>>> + Send>> + Send + Sync>)>

Handlers for events other modules emit; registered at Harness::build.

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> DynClone for T
where T: Clone,

Source§

fn __clone_box(&self, _: Private) -> *mut ()

Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> FromRef<T> for T
where T: Clone,

Source§

fn from_ref(input: &T) -> T

Converts to this type from a reference to the input type.
Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self> ⓘ

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self> ⓘ

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self> ⓘ
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self> ⓘ

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more