Documentation
¶
Overview ¶
Package webhookdelivery defines supported-adapter outbound webhook delivery contracts for tenant-scoped API services.
Use Catalog, Dispatcher, EndpointStore, DeliveryStore, SecretResolver, and async worker helpers to sign, dispatch, retry, and replay app-owned webhook deliveries. The package owns provider-neutral contracts and safe headers; it does not choose persistence, tenant mapping, or receiver-specific schemas.
Keep signing secrets and raw payloads out of logs and metrics. Retry classification and replay paths must stay tenant-scoped and fail closed on mismatches.
Index ¶
- Constants
- Variables
- func DefaultDeliveryID(event Event, endpoint Endpoint) string
- func EncodeJobPayload(payload JobPayload) ([]byte, error)
- func EndpointMatches(endpoint Endpoint, event Event) bool
- func Replay(ctx context.Context, store Replayer, cmd ReplayCommand) error
- func SafeHeaders(headers http.Header) (http.Header, error)
- func SafeLabel(value string) string
- func ValidateDelivery(delivery Delivery) error
- func ValidateEndpoint(endpoint Endpoint, policy EndpointPolicy) error
- func ValidateEvent(event Event) error
- type AttemptRecorder
- type AttemptResult
- type Catalog
- type Deliverer
- type DelivererConfig
- type Delivery
- type DeliveryEnqueuer
- type DeliveryObservation
- type DeliveryState
- type Dispatcher
- type DispatcherConfig
- type Endpoint
- type EndpointGetter
- type EndpointPolicy
- type EndpointRegistry
- type Event
- type HTTPDoer
- type Handler
- type HandlerConfig
- type JobPayload
- type MetricsRecorder
- type MetricsRecorderFunc
- type ReplayCommand
- type Replayer
- type RetryPolicy
Constants ¶
const (
// AnyEventType subscribes an endpoint to every event type in the catalog.
AnyEventType = "*"
)
Variables ¶
var ( // ErrInvalidEndpoint reports a webhook endpoint with missing or unsafe fields. ErrInvalidEndpoint = errors.New("invalid webhook endpoint") // ErrInvalidEvent reports a webhook event with missing or malformed fields. ErrInvalidEvent = errors.New("invalid webhook event") // ErrUnsupportedEvent reports an event type not present in the event catalog. ErrUnsupportedEvent = errors.New("unsupported webhook event") // ErrUnsafeHeader reports endpoint headers that could carry secrets or break signing. ErrUnsafeHeader = errors.New("unsafe webhook header") // ErrInvalidDelivery reports a malformed delivery or replay command. ErrInvalidDelivery = errors.New("invalid webhook delivery") // ErrTenantMismatch reports a job, event, endpoint, or delivery tenant mismatch. ErrTenantMismatch = errors.New("webhook tenant mismatch") // ErrDeliveryNotFound reports a missing tenant-scoped endpoint or delivery. ErrDeliveryNotFound = errors.New("webhook delivery not found") // ErrDeliveryFailed reports a delivery attempt that was not accepted by the receiver. ErrDeliveryFailed = errors.New("webhook delivery failed") // ErrStoreNotConfigured reports missing registry, store, or recorder dependencies. ErrStoreNotConfigured = errors.New("webhook delivery store not configured") )
Functions ¶
func DefaultDeliveryID ¶
DefaultDeliveryID returns a deterministic idempotency-friendly delivery id for an event/endpoint pair.
func EncodeJobPayload ¶
func EncodeJobPayload(payload JobPayload) ([]byte, error)
EncodeJobPayload validates and encodes the safe async delivery payload.
func EndpointMatches ¶
EndpointMatches verifies tenant, disabled state, and event subscription.
func Replay ¶
func Replay(ctx context.Context, store Replayer, cmd ReplayCommand) error
Replay schedules a tenant-scoped delivery for another attempt.
func SafeHeaders ¶
SafeHeaders validates and clones endpoint static headers. Secret-bearing, hop-by-hop, and signing headers are rejected.
func ValidateDelivery ¶
ValidateDelivery verifies required durable delivery fields.
func ValidateEndpoint ¶
func ValidateEndpoint(endpoint Endpoint, policy EndpointPolicy) error
ValidateEndpoint verifies required endpoint fields, URL policy, subscriptions, and safe static headers.
func ValidateEvent ¶
ValidateEvent verifies a webhook event without applying catalog policy.
Types ¶
type AttemptRecorder ¶
type AttemptRecorder interface {
RecordAttempt(ctx context.Context, result AttemptResult) error
}
AttemptRecorder records a durable delivery attempt.
type AttemptResult ¶
type AttemptResult struct {
DeliveryID string `json:"delivery_id"`
TenantID string `json:"tenant_id"`
EndpointID string `json:"endpoint_id"`
EventID string `json:"event_id"`
EventType string `json:"event_type"`
Attempt int `json:"attempt"`
StatusCode int `json:"status_code,omitempty"`
StatusClass string `json:"status_class"`
Accepted bool `json:"accepted"`
Retryable bool `json:"retryable"`
Error string `json:"error,omitempty"`
OccurredAt time.Time `json:"occurred_at"`
}
AttemptResult describes one outbound delivery attempt.
type Catalog ¶
type Catalog struct {
// contains filtered or unexported fields
}
Catalog is the allow-list of outbound event types a service can publish.
func NewCatalog ¶
NewCatalog creates a fail-closed event catalog.
func (Catalog) ValidateEvent ¶
ValidateEvent validates required event fields and catalog membership.
type Deliverer ¶
type Deliverer struct {
// contains filtered or unexported fields
}
Deliverer signs and sends outbound webhook HTTP requests.
func NewDeliverer ¶
func NewDeliverer(cfg DelivererConfig) (*Deliverer, error)
NewDeliverer constructs an HTTP webhook deliverer.
type DelivererConfig ¶
type DelivererConfig struct {
Client HTTPDoer
Clock func() time.Time
EndpointPolicy EndpointPolicy
SignatureHeader string
EventIDHeader string
TimestampHeader string
UserAgent string
Metrics MetricsRecorder
}
DelivererConfig configures the HTTP webhook deliverer.
type Delivery ¶
type Delivery struct {
ID string `json:"id"`
TenantID string `json:"tenant_id"`
EndpointID string `json:"endpoint_id"`
EventID string `json:"event_id"`
EventType string `json:"event_type"`
URL string `json:"url"`
State DeliveryState `json:"state"`
Attempt int `json:"attempt"`
NextAt time.Time `json:"next_at"`
LastStatusCode int `json:"last_status_code,omitempty"`
LastError string `json:"last_error,omitempty"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
Delivery is a tenant-scoped durable delivery record.
type DeliveryEnqueuer ¶
type DeliveryEnqueuer interface {
EnqueueDelivery(ctx context.Context, delivery Delivery, job JobPayload) error
}
DeliveryEnqueuer creates a durable delivery and queues its async job.
type DeliveryObservation ¶
DeliveryObservation is a low-cardinality metrics/logging event.
type DeliveryState ¶
type DeliveryState string
DeliveryState describes durable outbound webhook delivery state.
const ( // StatePending means a delivery is queued for the worker. StatePending DeliveryState = "pending" // StateLeased means a worker owns the current attempt. StateLeased DeliveryState = "leased" // StateSucceeded means the receiver accepted the delivery. StateSucceeded DeliveryState = "succeeded" // StateFailed means the delivery attempt failed but can be retried. StateFailed DeliveryState = "failed" // StateDeadLetter means retry policy has been exhausted. StateDeadLetter DeliveryState = "dead_letter" )
type Dispatcher ¶
type Dispatcher struct {
// contains filtered or unexported fields
}
Dispatcher creates durable deliveries for subscribed tenant endpoints.
func NewDispatcher ¶
func NewDispatcher(cfg DispatcherConfig) (*Dispatcher, error)
NewDispatcher constructs a Dispatcher.
type DispatcherConfig ¶
type DispatcherConfig struct {
Catalog Catalog
Endpoints EndpointRegistry
Store DeliveryEnqueuer
Clock func() time.Time
DeliveryIDFunc func(Event, Endpoint) string
EndpointPolicy EndpointPolicy
}
DispatcherConfig configures a Dispatcher.
type Endpoint ¶
type Endpoint struct {
ID string `json:"id"`
TenantID string `json:"tenant_id"`
URL string `json:"url"`
SigningSecret []byte `json:"-"`
Events []string `json:"events"`
Headers http.Header `json:"headers,omitempty"`
Disabled bool `json:"disabled,omitempty"`
CreatedAt time.Time `json:"created_at,omitempty"`
UpdatedAt time.Time `json:"updated_at,omitempty"`
}
Endpoint is a tenant-scoped outbound webhook target.
SigningSecret is intentionally omitted from JSON so durable jobs and logs do not accidentally persist raw endpoint secrets.
func (Endpoint) SubscribedTo ¶
SubscribedTo reports whether the endpoint subscribes to eventType.
type EndpointGetter ¶
type EndpointGetter interface {
GetEndpoint(ctx context.Context, tenantID, endpointID string) (Endpoint, bool, error)
}
EndpointGetter loads one tenant-scoped endpoint.
type EndpointPolicy ¶
type EndpointPolicy struct {
AllowInsecureHTTP bool
}
EndpointPolicy configures endpoint validation. HTTPS is required by default; tests and local-only development can explicitly allow HTTP.
type EndpointRegistry ¶
type EndpointRegistry interface {
ListEndpoints(ctx context.Context, tenantID, eventType string) ([]Endpoint, error)
}
EndpointRegistry lists tenant-scoped endpoints for an event type.
type Event ¶
type Event struct {
ID string `json:"id"`
TenantID string `json:"tenant_id"`
Type string `json:"type"`
Payload json.RawMessage `json:"payload"`
OccurredAt time.Time `json:"occurred_at,omitempty"`
}
Event is the durable outbound webhook event envelope used by dispatchers and async worker jobs. Payload must be valid JSON.
type Handler ¶
type Handler struct {
// contains filtered or unexported fields
}
Handler loads endpoints, sends jobs, and records attempt metadata for async.Runner.
func NewHandler ¶
func NewHandler(cfg HandlerConfig) (*Handler, error)
NewHandler constructs an async delivery handler.
type HandlerConfig ¶
type HandlerConfig struct {
Endpoints EndpointGetter
Deliverer *Deliverer
Attempts AttemptRecorder
}
HandlerConfig configures an async delivery handler.
type JobPayload ¶
type JobPayload struct {
DeliveryID string `json:"delivery_id"`
EndpointID string `json:"endpoint_id"`
Event Event `json:"event"`
}
JobPayload is the safe async payload for a delivery worker. It does not carry endpoint secrets; workers load the tenant-scoped endpoint before signing.
func DecodeJobPayload ¶
func DecodeJobPayload(payload []byte) (JobPayload, error)
DecodeJobPayload decodes and validates a worker job payload.
type MetricsRecorder ¶
type MetricsRecorder interface {
ObserveWebhookDelivery(ctx context.Context, event DeliveryObservation)
}
MetricsRecorder records low-cardinality delivery outcomes.
type MetricsRecorderFunc ¶
type MetricsRecorderFunc func(context.Context, DeliveryObservation)
MetricsRecorderFunc adapts a function to MetricsRecorder.
func (MetricsRecorderFunc) ObserveWebhookDelivery ¶
func (f MetricsRecorderFunc) ObserveWebhookDelivery(ctx context.Context, event DeliveryObservation)
ObserveWebhookDelivery records an outbound webhook event.
type ReplayCommand ¶
ReplayCommand requests tenant-scoped redelivery of a durable delivery.
type Replayer ¶
type Replayer interface {
ReplayDelivery(ctx context.Context, tenantID, deliveryID string, nextAt time.Time) error
}
Replayer moves a tenant-scoped delivery back into pending state.
type RetryPolicy ¶
RetryPolicy computes bounded exponential retry delays.