Help us shape the future of Go! Take the 2026 Go Developer Survey

webhook

package
v0.1.0 Latest Latest
Warning

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

Go to latest
Published: May 10, 2026 License: AGPL-3.0 Imports: 23 Imported by: 0

Documentation

Index

Constants

View Source
const (
	KindWebhookFanout   worker.Kind = "webhook:fanout"
	KindWebhookDeliver  worker.Kind = "webhook:deliver"
	KindWebhookPurgeOld worker.Kind = "webhook:purge_old"
)

Job kinds shipped by S33. Registered by cmd/shithubd/worker.go.

View Source
const BackoffBase = 30 * time.Second

BackoffBase is the smallest retry delay; doubles per attempt.

View Source
const BackoffMax = 24 * time.Hour

BackoffMax caps the delay so a stale subscriber doesn't disappear for days at a time. The spec calls for 24 hours.

View Source
const FanoutBatch = 200

FanoutBatch caps how many events a single tick drains.

View Source
const FanoutConsumer = "webhook_fanout"

FanoutConsumer is the consumer name in domain_events_processed. Distinct from notif's so cursors don't collide.

View Source
const MaxBodySize = 25 * 1024 * 1024

MaxBodySize caps the request body at 25 MiB per the spec. We don't build payloads anywhere near this, but the cap protects against pathological event shapes.

View Source
const ResponseBodyCap = 32 * 1024

ResponseBodyCap bounds the response body we keep in DB so a malicious receiver can't blow up our storage. Above this we set response_truncated=true.

View Source
const SecretLength = 32

SecretLength is the byte length of a freshly minted webhook secret. 32 bytes ≈ 256 bits — well above HMAC-SHA256's effective security.

Variables

View Source
var (
	ErrBadURL         = errors.New("webhook: URL must be http or https with a host")
	ErrBadContentType = errors.New("webhook: content_type must be json or form")
	ErrBadOwnerKind   = errors.New("webhook: owner_kind must be repo or org")
	ErrBadEvent       = errors.New("webhook: event names must be 1–64 lowercase chars")
)

CreateErrors surfaces user-friendly validation failures so handlers can render them in-page.

Functions

func Backoff

func Backoff(attempt int, jitter func() float64) time.Duration

Backoff returns the delay before retrying delivery `attempt` (1- indexed: the just-failed attempt counts as 1). Applies ±20% jitter so a fleet of webhooks pointed at one outage doesn't synchronize retries. `jitter` returns a value in [0, 1); pass `nil` for no jitter (deterministic schedule, useful in tests).

func Create

func Create(ctx context.Context, deps ManageDeps, params CreateParams) (webhookdb.Webhook, error)

Create persists a new webhook + emits a synthetic ping delivery so the operator sees an immediate round-trip. Returns the created row.

func Delete

func Delete(ctx context.Context, deps ManageDeps, hookID int64) error

Delete removes a webhook + cascades its deliveries (FK ON DELETE CASCADE).

func Deliver

func Deliver(ctx context.Context, deps DeliverDeps, deliveryID int64) error

Deliver handles one delivery row end-to-end: load + decrypt secret, validate URL via SSRF defense, POST, record outcome, schedule retry or mark terminal. Returns nil on success, an error to be logged on transient failure (the row is updated regardless).

func EnqueuePing

func EnqueuePing(ctx context.Context, deps FanoutDeps, hookID int64) error

EnqueuePing creates a synthetic ping delivery and enqueues it. Used on webhook create / re-enable so the operator sees an immediate round-trip.

func FanoutOnce

func FanoutOnce(ctx context.Context, deps FanoutDeps) (int, error)

FanoutOnce drains domain_events past the persisted cursor, finds matching webhooks, and inserts a webhook_deliveries row + enqueues a `webhook:deliver` job per match. Returns the number of events processed; the caller decides to re-enqueue when full.

The consumer cursor is advanced after each event regardless of whether the event matched any subscribers — an event without subscribers is "processed" the same as one with five.

func GenerateSecret

func GenerateSecret() (string, error)

GenerateSecret returns a hex-encoded random secret suitable for dropping into the webhook config form. The hex shape keeps it easy to paste into receiver code.

func IsSSRF

func IsSSRF(err error) bool

IsSSRF reports whether err is or wraps an SSRFError.

func MarshalDeliverPayload

func MarshalDeliverPayload(deliveryID int64) ([]byte, error)

MarshalDeliverPayload exposes the json shape so downstream packages (worker registration, tests) build the same payload without duplicating the struct.

func OpenSecret

func OpenSecret(box *secretbox.Box, ciphertext, nonce []byte) (string, error)

OpenSecret decrypts the stored ciphertext+nonce. Decryption failure is a hard error: the deliverer disables the webhook with a precise reason rather than silently signing with garbage.

func Redeliver

func Redeliver(ctx context.Context, deps FanoutDeps, originalID int64) (int64, error)

Redeliver clones a past delivery into a new pending row so the UI's "redeliver" button preserves the audit trail (redeliver_of points at the original).

func SealSecret

func SealSecret(box *secretbox.Box, secret string) (ciphertext, nonce []byte, err error)

SealSecret AEAD-wraps secret under the supplied box. Returns (ciphertext, nonce) for storage in webhooks.secret_ciphertext + webhooks.secret_nonce.

func SetActive

func SetActive(ctx context.Context, deps ManageDeps, hookID int64, active bool) error

SetActive toggles the active flag, clearing any auto-disable state.

func SignSHA256

func SignSHA256(secret, body []byte) string

SignSHA256 returns the X-Shithub-Signature-256 header value for the given body and per-webhook secret. The format mirrors GitHub's (`sha256=<hex>`) so existing receiver libraries verify cleanly.

func UnmarshalDeliverPayload

func UnmarshalDeliverPayload(raw json.RawMessage) (int64, error)

UnmarshalDeliverPayload extracts the id from a worker job's raw JSON. Used by the registered handler.

func Update

func Update(ctx context.Context, deps ManageDeps, hookID int64, params UpdateParams) error

Update applies a config change to a webhook. Returns ErrBadURL etc. on validation failure.

func VerifySHA256

func VerifySHA256(secret, body []byte, sig string) bool

VerifySHA256 returns true when sig matches HMAC-SHA256(secret, body). Uses constant-time compare so the verifier doesn't leak timing info. Provided as the receiver-side helper that test code (and any future inbound webhook surface) can reuse.

Types

type CreateParams

type CreateParams struct {
	OwnerKind   string // "repo" or "org"
	OwnerID     int64
	URL         string
	ContentType string // "json" or "form"
	Events      []string
	Secret      string
	Active      bool
	SSL         bool
	ActorUserID int64
}

CreateParams is the create-form input. Empty Secret means "mint one"; a non-empty value comes through verbatim from the operator (matches GitHub's behaviour where the user can paste their own).

type DeliverDeps

type DeliverDeps struct {
	Pool       *pgxpool.Pool
	Logger     *slog.Logger
	HTTPClient *http.Client
	SecretBox  *secretbox.Box
	SSRF       SSRFConfig
	// JitterFn is plumbed for tests; nil => math/rand.Float64.
	JitterFn func() float64
}

DeliverDeps wires the deliverer.

type FanoutDeps

type FanoutDeps struct {
	Pool   *pgxpool.Pool
	Logger *slog.Logger
}

FanoutDeps wires the fan-out against the runtime.

type ManageDeps

type ManageDeps struct {
	Pool      *pgxpool.Pool
	SecretBox *secretbox.Box
	SSRF      SSRFConfig
}

ManageDeps wires the create/update/delete orchestrator.

type SSRFConfig

type SSRFConfig = ssrf.Config

SSRFConfig is the re-exported alias for ssrf.Config.

func DefaultSSRFConfig

func DefaultSSRFConfig() SSRFConfig

DefaultSSRFConfig returns the production defaults.

type SSRFError

type SSRFError = ssrf.Error

SSRFError is the re-exported alias for ssrf.Error.

type UpdateParams

type UpdateParams struct {
	URL         string
	ContentType string
	Events      []string
	Active      bool
	SSL         bool
	NewSecret   string // when non-empty, rotate the secret
}

UpdateParams mirrors the edit form.

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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