webhook

package
v0.10.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 20, 2026 License: MIT Imports: 38 Imported by: 0

Documentation

Overview

Package webhook implements the reference server's native webhook contract, subscription management, encrypted capture and receiver authentication.

Delivery uses eventstore's persistent leases. Payloads are immutable for a delivery; replay creates another delivery for the same occurrence. Network delivery is at least once and unordered. Consumers deduplicate delivery IDs. Full decoded bodies and original bytes require root-managed subscriptions.

Index

Constants

View Source
const (
	HeaderID        = "webhook-id"
	HeaderTimestamp = "webhook-timestamp"
	HeaderSignature = "webhook-signature"
)
View Source
const (
	SchemaVersion            = "1"
	InlineLimit              = 256 << 10
	DefaultMaxBody           = 16 << 20
	DefaultPayloadRetention  = 7 * 24 * time.Hour
	DefaultMetadataRetention = 30 * 24 * time.Hour
	PayloadPath              = "/webhooks/v1/payloads/"
)

Variables

View Source
var (
	ErrInvalid   = errors.New("webhook: invalid argument")
	ErrNotFound  = errors.New("webhook: not found")
	ErrForbidden = errors.New("webhook: root or receiver authority required")
	ErrConflict  = errors.New("webhook: conflicting revision or delivery state")
	ErrExpired   = errors.New("webhook: payload expired")
)

Functions

func BlobColumns

func BlobColumns() []sqlcommon.BlobColumn

BlobColumns registers every sealed column for backup verification and rekey.

func ClassifyRoute

func ClassifyRoute(pattern, path string) (family, operation string)

ClassifyRoute deliberately excludes administration, internal DDM forwarding, health endpoints and payload retrieval. A missing route produces no event.

func CorrelationID

func CorrelationID(ctx context.Context) string

func ObserveMDM

func ObserveMDM(ctx context.Context, ck *mdm.Checkin, resp *mdm.Response, accepted bool)

ObserveMDM records decoded vocabulary and the independently determined service result. HTTP 200 alone is never interpreted as successful MDM authorization.

func ObserveOutcome

func ObserveOutcome(ctx context.Context, outcome string)

ObserveOutcome records the service result independently of its HTTP status.

func ObserveSubject

func ObserveSubject(ctx context.Context, subject Subject)

ObserveSubject records a resource only after the server has authenticated it.

func OutcomeType

func OutcomeType(internal string) string

func Sign

func Sign(secret, id string, at time.Time, body []byte) (string, error)

Sign follows Standard Webhooks: id.timestamp.body with a v1 HMAC-SHA256.

func Verify

func Verify(secret string, headers http.Header, body []byte, now time.Time) error

Verify authenticates the exact request bytes within a five-minute freshness window. Receivers must also persist delivery IDs to deduplicate retries.

func WithCorrelation

func WithCorrelation(ctx context.Context, id string) context.Context

WithCorrelation is for authenticated internal transports. Public ingress always replaces caller-provided identifiers before processing a request.

Types

type CatalogueEntry

type CatalogueEntry struct {
	Type          string   `json:"type"`
	Operations    []string `json:"operations,omitempty"`
	SummaryFields []string `json:"summary_fields"`
	FullJSON      bool     `json:"full_json"`
	Raw           bool     `json:"raw"`
}

func Catalogue

func Catalogue() []CatalogueEntry

Catalogue returns a fresh copy of the native vocabulary.

type Change

type Change struct {
	Subscription Subscription `json:"subscription"`
	Credentials  *Credentials `json:"credentials,omitempty"`
}

type Config

type Config struct {
	Enabled           bool
	Source            string
	PayloadRetention  time.Duration
	MetadataRetention time.Duration
	MaxBody           int
	PrivateNetworks   []string
	RootCAFile        string
	Client            *http.Client
	Now               func() time.Time
	// CommandType looks up already-authorized command metadata in the caller's
	// transaction. Missing/cleared commands return an empty type.
	CommandType func(context.Context, mdm.EnrollmentID, string) (string, error)
}

Config controls capture bounds and transport. PrivateNetworks explicitly opts an operator's trusted receiver networks into the outbound dial policy.

type Credentials

type Credentials struct {
	SigningSecret string `json:"signing_secret"`
	PayloadToken  string `json:"payload_token"`
}

Credentials are returned only when created or rotated; ordinary reads omit them.

type Delivery

type Delivery struct {
	ID             string    `json:"id"`
	EventID        string    `json:"event_id"`
	SubscriptionID string    `json:"subscription_id"`
	Revision       int       `json:"revision"`
	State          string    `json:"state"`
	Attempts       int       `json:"attempts"`
	NextAttempt    time.Time `json:"next_attempt"`
	LastCode       string    `json:"last_code"`
	ExpiresAt      time.Time `json:"expires_at"`
	Sensitive      bool      `json:"sensitive"`
	Event          Event     `json:"event"`
}

type Event

type Event struct {
	SchemaVersion string             `json:"schema_version"`
	EventID       string             `json:"event_id"`
	Type          string             `json:"type"`
	OccurredAt    time.Time          `json:"occurred_at"`
	Source        string             `json:"source"`
	Subject       *Subject           `json:"subject,omitempty"`
	CorrelationID string             `json:"correlation_id,omitempty"`
	Data          map[string]any     `json:"data"`
	Payloads      map[string]Payload `json:"payloads,omitempty"`
}

Event is the immutable occurrence carried by one or more deliveries.

type Filters

type Filters struct {
	Operations   []string `json:"operations,omitempty"`
	Outcomes     []string `json:"outcomes,omitempty"`
	Channels     []string `json:"channels,omitempty"`
	Subjects     []string `json:"subjects,omitempty"`
	CommandTypes []string `json:"command_types,omitempty"`
}

Filters use OR within a field and AND between fields. Subject filters never match unverified claims or exchanges without a verified subject.

type Payload

type Payload struct {
	ContentType  string          `json:"content_type"`
	Encoding     string          `json:"encoding"`
	Availability string          `json:"availability"`
	Size         int             `json:"size"`
	SHA256       string          `json:"sha256,omitempty"`
	Value        json.RawMessage `json:"value,omitempty"`
	Href         string          `json:"href,omitempty"`
	ExpiresAt    *time.Time      `json:"expires_at,omitempty"`
}

Payload is either inline, referenced, or explicitly unavailable. SHA256 and Size describe the representation fetched from Href (raw bytes or JSON).

func BodyPayload

func BodyPayload(body []byte, contentType string, asJSON bool) Payload

BodyPayload preserves bytes without a lossy intermediate number conversion.

type PayloadPolicy

type PayloadPolicy struct {
	FullJSON    bool `json:"full_json"`
	RawRequest  bool `json:"raw_request"`
	RawResponse bool `json:"raw_response"`
}

PayloadPolicy is a disclosure ceiling, including when replaying older data.

func (PayloadPolicy) Sensitive

func (p PayloadPolicy) Sensitive() bool

type ReplayRequest

type ReplayRequest struct {
	SubscriptionID string    `json:"subscription_id"`
	EventIDs       []string  `json:"event_ids,omitempty"`
	Type           string    `json:"type,omitempty"`
	After          time.Time `json:"after,omitempty"`
	Before         time.Time `json:"before,omitempty"`
	Key            string    `json:"key"`
	DryRun         bool      `json:"dry_run"`
	Limit          int       `json:"limit,omitempty"`
}

type ReplayResult

type ReplayResult struct {
	EventIDs    []string            `json:"event_ids"`
	DeliveryIDs []string            `json:"delivery_ids"`
	DryRun      bool                `json:"dry_run"`
	Truncated   bool                `json:"truncated"`
	Missing     map[string][]string `json:"missing_payloads,omitempty"`
}

type Spec

type Spec struct {
	Name    string        `json:"name"`
	URL     string        `json:"url"`
	Events  []string      `json:"events"`
	Filters Filters       `json:"filters"`
	Payload PayloadPolicy `json:"payload"`
}

Spec is the writable subscription configuration. URLs may contain secrets and are only exposed to webhook administrators, never through delivery diagnostics.

type Store

type Store struct {
	// contains filtered or unexported fields
}

func Open

func Open(ctx context.Context, db *sql.DB, d sqlcommon.Dialect, keys *crypt.Keyring, outbox *eventstore.Store, cfg Config) (*Store, error)

Open requires encryption even for summary-only subscriptions because signing keys are recoverable secrets. It never owns the supplied SQL pool.

func (*Store) Admin

func (s *Store) Admin(w http.ResponseWriter, r *http.Request, root bool)

Admin serves after the host application's ordinary Cedar authorization. Root remains a separate mandatory gate for sensitive destination operations.

func (*Store) Capture

func (s *Store) Capture(ctx context.Context, e Event) error

Capture participates in the caller's SQL transaction. It copies payloads at capture time, never retains mutable pointers, and records nothing when no enabled subscription matches.

func (*Store) CaptureOutcome

func (s *Store) CaptureOutcome(ctx context.Context, in event.Event) error

CaptureOutcome adapts existing in-process events without changing library APIs. The projection remains the summary; complete internal data is a distinct, root-controlled representation.

func (*Store) Create

func (s *Store) Create(ctx context.Context, spec Spec, root bool) (Change, error)

func (*Store) Deliveries

func (s *Store) Deliveries(ctx context.Context, id, after string, limit int) ([]Delivery, error)

func (*Store) Get

func (s *Store) Get(ctx context.Context, id string) (Subscription, error)

func (*Store) List

func (s *Store) List(ctx context.Context, after string, limit int) ([]Subscription, error)

func (*Store) Observe

func (s *Store) Observe(next http.Handler, mux *http.ServeMux) http.Handler

Observe captures only bytes consumed/written by the handler. It never drains rejected bodies or changes protocol responses when observation persistence fails. Capture failures remain visible through Status.

func (*Store) ObserveCertificates

func (s *Store) ObserveCertificates(repository state.Store) state.Store

ObserveCertificates joins SQL-backed managed certificate transitions and capture in one transaction. The supplied store must use this Store's SQL pool. The concrete state.Tx reaches the callback unchanged, preserving publication adapters; private material is never extracted from state records.

func (*Store) PayloadHandler

func (s *Store) PayloadHandler() http.Handler

PayloadHandler authenticates only subscription-scoped receiver credentials. It accepts no administrator tokens and never grants administrative access.

func (*Store) Prune

func (s *Store) Prune(ctx context.Context) error

Prune expires payloads before deleting metadata. Replay never changes expiry.

func (*Store) Replay

func (s *Store) Replay(ctx context.Context, req ReplayRequest, root bool) (ReplayResult, error)

func (*Store) Resolve

func (s *Store) Resolve(_ context.Context, dest string) (eventstore.Sender, error)

Resolve integrates native deliveries with the existing persistent worker.

func (*Store) Retry

func (s *Store) Retry(ctx context.Context, id string, root bool) error

func (*Store) Rewrap

func (s *Store) Rewrap(ctx context.Context) (int, error)

func (*Store) Rotate

func (s *Store) Rotate(ctx context.Context, id string, overlap time.Duration, root bool) (Change, error)

func (*Store) RunRetention

func (s *Store) RunRetention(ctx context.Context) error

func (*Store) Send

func (s *Store) Send(ctx context.Context, id string) error

func (*Store) SetState

func (s *Store) SetState(ctx context.Context, id, action string, root bool) (Subscription, error)

func (*Store) Status

func (s *Store) Status(ctx context.Context) (map[string]any, error)

func (*Store) Test

func (s *Store) Test(ctx context.Context, id string, root bool) (string, error)

Test queues a synthetic occurrence directly to the selected subscription.

func (*Store) Update

func (s *Store) Update(ctx context.Context, id string, revision int, spec Spec, root bool) (Change, error)

Update uses an expected revision to prevent lost operator updates. Old deliveries remain paused; only explicit replay can move them to the new URL.

type Subject

type Subject struct {
	Kind     string `json:"kind"`
	ID       string `json:"id"`
	Channel  string `json:"channel,omitempty"`
	ParentID string `json:"parent_id,omitempty"`
}

Subject identifies a verified resource. Unverified device claims belong in Data.

type Subscription

type Subscription struct {
	ID       string `json:"id"`
	Revision int    `json:"revision"`
	Spec
	Enabled   bool      `json:"enabled"`
	Paused    bool      `json:"paused"`
	Deleted   bool      `json:"deleted"`
	CreatedAt time.Time `json:"created_at"`
	UpdatedAt time.Time `json:"updated_at"`
}

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL