Documentation
¶
Overview ¶
Package webhook implements the core types, HTTP client, HMAC signing, and error handling for ToolHive's dynamic webhook middleware system.
Index ¶
- Constants
- func SignPayload(secret []byte, timestamp int64, payload []byte) string
- func VerifySignature(secret []byte, timestamp int64, payload []byte, signature string) bool
- type Client
- type Config
- type FailurePolicy
- type InvalidResponseError
- type MutatingResponse
- type NetworkError
- type Request
- type RequestContext
- type Response
- type TLSConfig
- type TimeoutError
- type Type
- type WebhookError
Constants ¶
const ( // SignatureHeader is the HTTP header containing the HMAC signature. SignatureHeader = "X-ToolHive-Signature" // TimestampHeader is the HTTP header containing the Unix timestamp. TimestampHeader = "X-ToolHive-Timestamp" )
Header names for webhook HMAC signing.
const APIVersion = "v0.1.0"
APIVersion is the version of the webhook API protocol.
const DefaultTimeout = 10 * time.Second
DefaultTimeout is the default timeout for webhook HTTP calls.
const MaxResponseSize = 1 << 20
MaxResponseSize is the maximum allowed size in bytes for webhook responses (1 MB).
const MaxTimeout = 30 * time.Second
MaxTimeout is the maximum allowed timeout for webhook HTTP calls.
Variables ¶
This section is empty.
Functions ¶
func SignPayload ¶
SignPayload computes an HMAC-SHA256 signature over the given timestamp and payload. The signature is computed over the string "timestamp.payload" and returned in the format "sha256=<hex-encoded-signature>".
func VerifySignature ¶
VerifySignature verifies an HMAC-SHA256 signature against the given timestamp and payload. The signature should be in the format "sha256=<hex-encoded-signature>". Comparison is done in constant time to prevent timing attacks.
Note: This function only verifies cryptographic correctness. Callers should independently verify that the timestamp is recent (e.g., within 5 minutes) to prevent replay attacks.
Types ¶
type Client ¶
type Client struct {
// contains filtered or unexported fields
}
Client is an HTTP client for calling webhook endpoints.
func NewClient ¶
NewClient creates a new webhook Client from the given configuration. The hmacSecret parameter is the resolved secret bytes for HMAC signing; pass nil if signing is not configured.
func (*Client) CallMutating ¶
CallMutating sends a request to a mutating webhook and returns its response.
type Config ¶
type Config struct {
// Name is a unique identifier for this webhook.
Name string `json:"name"`
// URL is the HTTPS endpoint to call.
URL string `json:"url"`
// Timeout is the maximum time to wait for a webhook response.
Timeout time.Duration `json:"timeout"`
// FailurePolicy determines behavior when the webhook call fails.
FailurePolicy FailurePolicy `json:"failure_policy"`
// TLSConfig holds optional TLS configuration (CA bundles, client certs).
TLSConfig *TLSConfig `json:"tls_config,omitempty"`
// HMACSecretRef is an optional reference to an HMAC secret for payload signing.
HMACSecretRef string `json:"hmac_secret_ref,omitempty"`
}
Config holds the configuration for a single webhook.
type FailurePolicy ¶
type FailurePolicy string
FailurePolicy defines how webhook errors are handled.
const ( // FailurePolicyFail denies the request on webhook error (fail-closed). FailurePolicyFail FailurePolicy = "fail" // FailurePolicyIgnore allows the request on webhook error (fail-open). FailurePolicyIgnore FailurePolicy = "ignore" )
type InvalidResponseError ¶
type InvalidResponseError struct {
WebhookError
// StatusCode is the HTTP status code returned by the webhook, if applicable.
// A value of 0 means no HTTP response was received (e.g., JSON decode error).
StatusCode int
}
InvalidResponseError indicates that a webhook returned an unparsable or invalid response.
func NewInvalidResponseError ¶
func NewInvalidResponseError(webhookName string, err error, statusCode int) *InvalidResponseError
NewInvalidResponseError creates a new InvalidResponseError. statusCode is the HTTP status code from the webhook response (0 if not applicable).
func (*InvalidResponseError) Error ¶
func (e *InvalidResponseError) Error() string
Error implements the error interface.
type MutatingResponse ¶
type MutatingResponse struct {
Response
// PatchType indicates the type of patch (e.g., "json_patch").
PatchType string `json:"patch_type,omitempty"`
// Patch contains the JSON Patch operations to apply.
Patch json.RawMessage `json:"patch,omitempty"`
}
MutatingResponse is the response from a mutating webhook.
type NetworkError ¶
type NetworkError struct {
WebhookError
}
NetworkError indicates a network-level failure when calling a webhook.
func NewNetworkError ¶
func NewNetworkError(webhookName string, err error) *NetworkError
NewNetworkError creates a new NetworkError.
func (*NetworkError) Error ¶
func (e *NetworkError) Error() string
Error implements the error interface.
type Request ¶
type Request struct {
// Version is the webhook API protocol version.
Version string `json:"version"`
// UID is a unique identifier for this request, used for idempotency.
UID string `json:"uid"`
// Timestamp is when the request was created.
Timestamp time.Time `json:"timestamp"`
// Principal contains the authenticated user's identity information.
// Uses PrincipalInfo (not Identity) so credentials never enter the webhook payload.
Principal *auth.PrincipalInfo `json:"principal"`
// MCPRequest is the raw MCP JSON-RPC request.
MCPRequest json.RawMessage `json:"mcp_request"`
// Context provides additional metadata about the request origin.
Context *RequestContext `json:"context"`
}
Request is the payload sent to webhook endpoints.
type RequestContext ¶
type RequestContext struct {
// ServerName is the ToolHive/vMCP instance name handling the request.
ServerName string `json:"server_name"`
// BackendServer is the actual MCP server being proxied (when using vMCP).
BackendServer string `json:"backend_server,omitempty"`
// Namespace is the Kubernetes namespace, if applicable.
Namespace string `json:"namespace,omitempty"`
// SourceIP is the client's IP address.
SourceIP string `json:"source_ip"`
// Transport is the connection transport type (e.g., "sse", "stdio").
Transport string `json:"transport"`
}
RequestContext provides metadata about the request origin and environment.
type Response ¶
type Response struct {
// Version is the webhook API protocol version.
Version string `json:"version"`
// UID is the unique request identifier, echoed back for correlation.
UID string `json:"uid"`
// Allowed indicates whether the request is permitted.
Allowed bool `json:"allowed"`
// Code is an optional HTTP status code for denied requests.
Code int `json:"code,omitempty"`
// Message is an optional human-readable explanation.
Message string `json:"message,omitempty"`
// Reason is an optional machine-readable denial reason.
Reason string `json:"reason,omitempty"`
// Details contains optional structured information about the denial.
Details map[string]string `json:"details,omitempty"`
}
Response is the response from a validating webhook.
type TLSConfig ¶
type TLSConfig struct {
// CABundlePath is the path to a CA certificate bundle for server verification.
CABundlePath string `json:"ca_bundle_path,omitempty"`
// ClientCertPath is the path to a client certificate for mTLS.
ClientCertPath string `json:"client_cert_path,omitempty"`
// ClientKeyPath is the path to a client key for mTLS.
ClientKeyPath string `json:"client_key_path,omitempty"`
// InsecureSkipVerify disables server certificate verification.
// WARNING: This should only be used for development/testing.
InsecureSkipVerify bool `json:"insecure_skip_verify,omitempty"`
}
TLSConfig holds TLS-related configuration for webhook HTTP communication.
type TimeoutError ¶
type TimeoutError struct {
WebhookError
}
TimeoutError indicates that a webhook call timed out.
func NewTimeoutError ¶
func NewTimeoutError(webhookName string, err error) *TimeoutError
NewTimeoutError creates a new TimeoutError.
func (*TimeoutError) Error ¶
func (e *TimeoutError) Error() string
Error implements the error interface.
type WebhookError ¶
type WebhookError struct {
// WebhookName is the name of the webhook that caused the error.
WebhookName string
// Err is the underlying error.
Err error
}
WebhookError is the base error type for all webhook-related errors.
func (*WebhookError) Error ¶
func (e *WebhookError) Error() string
Error implements the error interface.
func (*WebhookError) Unwrap ¶
func (e *WebhookError) Unwrap() error
Unwrap returns the underlying error for errors.Is/errors.As support.