webhooks

package
v1.801.218 Latest Latest
Warning

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

Go to latest
Published: Jul 26, 2026 License: Apache-2.0 Imports: 24 Imported by: 0

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

func Mount

func Mount(app *zip.App, deps cloud.Deps) error

Mount opens the per-org registry stores, wires /v1/webhooks, and starts the bus dispatcher (fail-soft). It never returns an error for a bus problem — only for a genuinely unusable Deps — so a messaging fault can never abort the binary's boot.

func Shutdown

func Shutdown(_ context.Context) error

Shutdown stops the dispatcher (draining its workers) and closes every open per-org store. Idempotent.

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.

Jump to

Keyboard shortcuts

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