Documentation
¶
Overview ¶
Package webhooks is the platform-global webhook layer (HIP-0106): ONE registry plus ONE dispatcher that delivers ANY event on the platform bus to org-registered HTTP subscribers. It supersedes commerce's local, billing-scoped delivery path — the webhook surface is /v1/webhooks, top-level, never under /v1/billing, and it fans out EVERY platform event, not just commerce's.
TWO ORTHOGONAL HALVES.
- REGISTRY (api.go, store.go) — /v1/webhooks CRUD. An org manages ONLY its own endpoints; each org's registry is a physically separate {DataDir}/orgs/{slug}/ webhooks.db (the clients/books per-org SQLite idiom via cloud.OrgStore), so one tenant can never read or mutate another's. The org is the VALIDATED principal (principal.Org, gateway-minted X-Org-Id), exactly like clients/notify — 401 for an unauthenticated caller.
- DISPATCHER (dispatch.go, match.go) — a durable JetStream consumer on cloud's own embedded bus (clients/pubsub, stream COMMERCE, subjects commerce.>). It resolves each event's org from the envelope, matches ONLY that org's active subscriptions (NATS subject-wildcard semantics), and POSTs each match with a fresh HMAC-SHA256 signature and a bounded retry ladder. Org isolation is by construction: the store lookup is per-org, so B's endpoint can never receive A's event.
FAIL-SOFT MOUNT. The registry always mounts. The dispatcher is best-effort: no bus URL ⇒ inert; a down bus ⇒ background reconnect-retry. A messaging fault never crashes the shared cloud binary.
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
Types ¶
type DeliveryRow ¶
type DeliveryRow struct {
EndpointID string `json:"endpoint"`
DeliveryID string `json:"delivery"`
Subject string `json:"subject"`
Attempt int `json:"attempt"`
Status string `json:"status"`
HTTPStatus int `json:"httpStatus"`
Error string `json:"error,omitempty"`
DurationMs int64 `json:"durationMs"`
Created string `json:"created"`
}
DeliveryRow is one recorded delivery attempt: the /:id/deliveries wire model AND the stored row. One attempt-group (a single event → one endpoint) writes one row per attempt, all sharing a Delivery id; Status is "ok" | "retrying" | "failed". HTTPStatus is 0 on a network/timeout error, Error is empty on success.
type Endpoint ¶
type Endpoint struct {
ID string `json:"id"`
Org string `json:"org"`
URL string `json:"url"`
Events []string `json:"events"`
Secret string `json:"secret,omitempty"`
Status string `json:"status"`
Description string `json:"description,omitempty"`
CreatedAt string `json:"created"`
UpdatedAt string `json:"updated"`
// Deliveries7d / Failures7d are cheap usage counters computed from the delivery log
// over usageWindow (not stored columns) and populated ONLY on list/get. They are 0
// when there is no delivery history — never omitempty, so the console always sees them.
Deliveries7d int `json:"deliveries7d"`
Failures7d int `json:"failures7d"`
}
Endpoint is one registered webhook subscriber. It is BOTH the wire model and the stored row. Secret is returned ONLY on create (json omitempty + cleared elsewhere), so the signing key leaves the server exactly once.