Documentation
¶
Overview ¶
Package webhook dispatches PromptZero lifecycle events as outbound HTTP POSTs. Operators configure per-event subscriptions in config.yaml (`webhooks:`) and events fire non-blocking from the agent's callbacks — tool completion, risk prompts, workflow completion, audit-critical rows, session lifecycle.
The dispatcher queues events to a bounded buffer, retries 5xx/transport errors with exponential backoff, and signs the body with HMAC-SHA256 when a per-subscription secret is set. Close(ctx) drains the queue so shutdown doesn't lose the trailing session_ended payload.
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func KnownEventNames ¶ added in v0.27.0
func KnownEventNames() []string
KnownEventNames returns the canonical list of event names sorted alphabetically. Useful for error messages and `/webhooks` help output so operators don't have to grep the source.
func ValidateSubscription ¶ added in v0.18.0
func ValidateSubscription(s Subscription) error
ValidateSubscription rejects subscription URLs that point at loopback, link-local, or RFC1918 destinations — webhook payloads carry tool inputs/outputs (potentially including captured credentials), so SSRF into the cloud-metadata endpoint (169.254.169.254), local Kubernetes API, or peer services on the host network is in scope.
To target an internal endpoint deliberately, set PROMPTZERO_WEBHOOK_ALLOW_INTERNAL=1 — the rejection becomes a warning.
Validation runs at config-load time so a misconfigured URL fails loudly instead of leaking on first event.
Types ¶
type Dispatcher ¶
type Dispatcher interface {
Fire(ev Event, payload any)
// FireByName delivers a payload to a single subscription identified
// by its `name:` field, bypassing the Events allowlist filter that
// Fire applies. Used by the rules engine where the operator's intent
// (`webhook: ops-pager` in a rule's then-block) is "deliver to this
// specific subscription" rather than "broadcast on this event type".
// Subscriptions without a matching name are silent — the call is
// fire-and-forget like Fire. The synthesised event tag is "rule_fired"
// so the receiver can still distinguish rule-driven deliveries from
// natural lifecycle events.
FireByName(name string, payload any)
Close(ctx context.Context) error
Subscriptions() []Subscription
RecentResults(name string) []SendResult
TestSubscription(ctx context.Context, name string) error
}
Dispatcher is the fire-and-forget interface the rest of the app sees. Fire never blocks (drops with a warning when the queue overflows).
func New ¶
func New(subs []Subscription) Dispatcher
New constructs a dispatcher. When subs is empty it returns a no-op dispatcher so callers don't branch on nil.
type Envelope ¶
type Envelope struct {
Event Event `json:"event"`
Timestamp time.Time `json:"ts"`
Payload any `json:"payload"`
}
Envelope is the JSON body shape every subscription receives.
type Event ¶
type Event string
Event is one of the seven lifecycle hooks a subscription can filter on. An empty Events list on a subscription means "all events".
const ( EventToolFinished Event = "tool_finished" EventRiskPrompted Event = "risk_prompted" EventRiskDenied Event = "risk_denied" EventWorkflowCompleted Event = "workflow_completed" EventAuditCritical Event = "audit_critical" EventSessionStarted Event = "session_started" EventSessionEnded Event = "session_ended" )
const EventRuleFired Event = "rule_fired"
EventRuleFired is the synthetic event tag the worker stamps on envelopes produced by FireByName. Receivers use it to distinguish rule-driven deliveries (one specific subscription) from natural lifecycle events (fan-out by Events filter).
type SendResult ¶
SendResult is the outcome of a single delivery attempt — used by /webhooks to render a short history per subscription.
type Subscription ¶
type Subscription struct {
Name string `yaml:"name"`
URL string `yaml:"url"`
Events []Event `yaml:"events"`
Headers map[string]string `yaml:"headers"`
Secret string `yaml:"secret"`
}
Subscription is one configured HTTP webhook target. A zero Events slice opts into every event; a non-empty slice is an allowlist.