webhook

package
v0.6.0 Latest Latest
Warning

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

Go to latest
Published: Jun 13, 2026 License: MIT Imports: 20 Imported by: 0

Documentation

Overview

Package webhook delivers lifecycle events (host enrolled/blocked/deleted, cert rotated, cert expiring, …) to operator-configured HTTP endpoints. It is the unified outbound-event bus: handlers and background scanners publish typed events via Emit; the Dispatcher signs and delivers them asynchronously, off the request path, with bounded retry (#256).

Targets come from two places: an optional static target from server config (phase 1) and managed subscriptions loaded from a SubscriptionSource (phase 2). Deliveries are SSRF-guarded at request time and signed with HMAC-SHA256. The signing/guard logic mirrors internal/alerts and internal/config.hostIsPrivate and must stay in sync with them.

Index

Constants

View Source
const (
	EventHeader     = "X-Nebula-Event"
	DeliveryHeader  = "X-Nebula-Delivery"
	SignatureHeader = "X-Nebula-Signature"
)

HTTP headers attached to every delivery. Receivers authenticate the body via SignatureHeader and deduplicate on DeliveryHeader.

Variables

This section is empty.

Functions

This section is empty.

Types

type Config

type Config struct {
	// Static config target (optional).
	URL          string
	HMACSecret   string
	AllowPrivate bool
	Events       []string // static target's event filter; empty = all

	// Managed subscriptions (optional).
	Source SubscriptionSource

	// Tunables — zero values fall back to the defaults below.
	QueueSize    int
	MaxRetries   int
	RetryBackoff time.Duration
	DeliveryTO   time.Duration

	// Test seams.
	HTTPClient *http.Client     // overrides both guarded/unguarded clients
	Now        func() time.Time // nil => time.Now
	NewID      func() string    // nil => "evt_"+uuid
}

Config configures a Dispatcher. A static target is delivered when URL is set (phase-1 config webhook); managed targets come from Source.

type Dispatcher

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

Dispatcher signs and delivers events asynchronously. Construct with New, publish with Emit, and Close on shutdown to drain in-flight deliveries.

func New

func New(cfg Config, logger *slog.Logger) *Dispatcher

New starts a Dispatcher and its delivery worker. The worker runs until Close.

func (*Dispatcher) Close

func (d *Dispatcher) Close()

Close stops accepting events and drains what is already queued. Idempotent.

func (*Dispatcher) Emit

func (d *Dispatcher) Emit(eventType string, data map[string]any)

Emit enqueues an event. It never blocks the caller: a full queue drops the event with a logged warning rather than stalling the request path. Per-target filtering happens at delivery. A nil Dispatcher (webhooks disabled) is a no-op.

type Emitter

type Emitter interface {
	Emit(eventType string, data map[string]any)
}

Emitter is the narrow interface handlers depend on. *Dispatcher implements it; a nil *Dispatcher Emit is a no-op.

type Event

type Event struct {
	ID        string         `json:"id"`
	Type      string         `json:"type"`
	CreatedAt time.Time      `json:"created_at"`
	Data      map[string]any `json:"data"`
}

Event is the envelope POSTed to a subscriber. Data carries the event-type-specific payload.

type StoreSource

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

StoreSource resolves managed subscriptions from the store, decrypting each subscription's HMAC secret under the master key at delivery time. It satisfies SubscriptionSource.

func NewStoreSource

func NewStoreSource(s SubStore, master *keystore.Master, logger *slog.Logger) *StoreSource

NewStoreSource builds a store-backed subscription source. master may be nil (subscriptions with secrets are then skipped with a logged error).

func (*StoreSource) RecordDelivery

func (s *StoreSource) RecordDelivery(ctx context.Context, id string, ok bool, errMsg string)

RecordDelivery persists a delivery outcome against the subscription.

func (*StoreSource) TargetsFor

func (s *StoreSource) TargetsFor(ctx context.Context, eventType string) ([]Target, error)

TargetsFor returns active subscriptions wanting eventType, secrets decrypted.

type SubStore

type SubStore interface {
	ListActiveWebhookSubscriptions(ctx context.Context) ([]*models.WebhookSubscription, error)
	RecordWebhookDelivery(ctx context.Context, id string, ok bool, errMsg string, at time.Time) error
}

SubStore is the store surface a StoreSource needs.

type SubscriptionSource

type SubscriptionSource interface {
	TargetsFor(ctx context.Context, eventType string) ([]Target, error)
	RecordDelivery(ctx context.Context, targetID string, ok bool, errMsg string)
}

SubscriptionSource yields managed subscriptions for an event and records the outcome of each delivery.

type Target

type Target struct {
	ID           string
	URL          string
	Secret       []byte // HMAC secret; empty => unsigned
	AllowPrivate bool
}

Target is one resolved delivery endpoint. ID is the subscription id for managed targets (delivery status is recorded against it) and empty for the static config target.

Jump to

Keyboard shortcuts

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