Documentation
¶
Overview ¶
Package webhook implements webhook signature verification for SDK consumers receiving FlowCatalyst webhooks.
The signing algorithm MUST match the router's sign_webhook in internal/router/mediator.go byte-for-byte. Both sides verify against a shared test vector in tests/golden/webhook/.
Index ¶
Constants ¶
const ( ValidatorSignatureHeader = "X-FlowCatalyst-Signature" ValidatorTimestampHeader = "X-FlowCatalyst-Timestamp" )
Header names accepted by the Validator. HTTP header lookups are case-insensitive, so these match the router's uppercase X-FLOWCATALYST-* headers in any compliant framework.
Both this Validator and Verifier expect the timestamp format the FlowCatalyst router actually emits: ISO8601 with millisecond precision (e.g. 2026-05-24T08:30:00.123Z). A bare Unix-seconds integer is also accepted for backward compatibility. The two types differ only in API style; prefer Verifier for new code.
const DefaultToleranceSecs = 300
DefaultToleranceSecs is how stale a timestamp may be before rejection.
const FutureGraceSecs = 60
FutureGraceSecs caps how far into the future a timestamp may be (clock skew between platform and consumer).
const SignatureHeader = "X-FLOWCATALYST-SIGNATURE"
SignatureHeader matches the router's outbound header name.
const TimestampHeader = "X-FLOWCATALYST-TIMESTAMP"
TimestampHeader matches the router's outbound header name.
Variables ¶
var ( ErrValidatorMissingSignature = errors.New("webhook: missing signature header (" + ValidatorSignatureHeader + ")") ErrValidatorMissingTimestamp = errors.New("webhook: missing timestamp header (" + ValidatorTimestampHeader + ")") ErrValidatorInvalidTimestamp = errors.New("webhook: invalid timestamp") ErrValidatorInvalidSignature = errors.New("webhook: invalid signature") ErrTimestampExpired = errors.New("webhook: timestamp expired") ErrTimestampInFuture = errors.New("webhook: timestamp is in the future") ErrMissingSecret = errors.New("webhook: signing secret not configured") )
Sentinel validation errors used by Validator. Use errors.Is to branch.
var ( ErrMissingSignature = errors.New("webhook: missing signature header") ErrMissingTimestamp = errors.New("webhook: missing timestamp header") ErrBadTimestamp = errors.New("webhook: malformed timestamp") ErrStaleTimestamp = errors.New("webhook: timestamp outside clock skew tolerance") ErrBadSignature = errors.New("webhook: signature does not match") )
Errors.
Functions ¶
This section is empty.
Types ¶
type Validator ¶
type Validator struct {
// contains filtered or unexported fields
}
Validator validates HMAC-SHA256 webhook signatures. The signed message is timestamp || payload (hex-encoded). The timestamp is the ISO8601 millisecond value emitted by the FlowCatalyst router (a bare Unix-seconds integer is also accepted for backward compatibility).
Validator and Verifier are functionally equivalent; they differ only in API style. Prefer Verifier for new code.
func NewValidator ¶
func NewValidator(secret string, opts ...ValidatorOption) *Validator
NewValidator builds a Validator with the given signing secret.
func ValidatorFromEnv ¶
func ValidatorFromEnv(opts ...ValidatorOption) (*Validator, error)
ValidatorFromEnv builds a Validator from FLOWCATALYST_SIGNING_SECRET. Returns ErrMissingSecret if the env var is unset or empty.
func (*Validator) ComputeSignature ¶
ComputeSignature renders the expected hex-encoded HMAC-SHA256 for a (timestamp, payload) pair. Exposed so consumers can sign outbound callbacks with the same algorithm.
func (*Validator) Validate ¶
Validate checks the signature against the body and timestamp.
- signature: value of X-FlowCatalyst-Signature header (hex HMAC-SHA256)
- timestamp: value of X-FlowCatalyst-Timestamp header (ISO8601 ms, e.g. 2026-05-24T08:30:00.123Z; a bare Unix-seconds integer is also accepted)
- payload: raw request body
Returns nil on success, or one of the sentinel errors. The signature comparison is constant-time.
type ValidatorOption ¶
type ValidatorOption func(*Validator)
ValidatorOption configures a Validator.
func WithClock ¶
func WithClock(now func() time.Time) ValidatorOption
WithClock overrides the time source. Tests use this to inject a fixed clock.
func WithTolerance ¶
func WithTolerance(d time.Duration) ValidatorOption
WithTolerance sets the timestamp freshness window. Default 300s.
type Verifier ¶
type Verifier struct {
// MaxClockSkew is how far the inbound timestamp may differ from now.
// Defaults to 5 minutes to allow for clock drift between sender and receiver.
MaxClockSkew time.Duration
// contains filtered or unexported fields
}
Verifier validates inbound webhook signatures.
func NewVerifier ¶
NewVerifier wires a verifier with the supplied signing secret.
func (*Verifier) Verify ¶
Verify validates the signature header against the supplied body.
`body` MUST be the raw bytes received over the wire (don't re-serialize — that would re-introduce JSON-library-dependent variation). `signature` is the hex from the X-FLOWCATALYST-SIGNATURE header. `timestamp` is the X-FLOWCATALYST-TIMESTAMP header.