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 IsAlwaysDenyError(err error) bool
- func SignPayload(secret []byte, timestamp int64, payload []byte) string
- func ValidateConfig(cfg *FileConfig) error
- func VerifySignature(secret []byte, timestamp int64, payload []byte, signature string) bool
- type Client
- type Config
- type FailurePolicy
- type FileConfig
- 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.
const MinTimeout = 1 * time.Second
MinTimeout is the minimum allowed timeout for webhook HTTP calls.
Variables ¶
This section is empty.
Functions ¶
func IsAlwaysDenyError ¶ added in v0.19.0
IsAlwaysDenyError reports whether the webhook error should deny the request regardless of the configured failure policy.
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 ValidateConfig ¶ added in v0.19.0
func ValidateConfig(cfg *FileConfig) error
ValidateConfig validates all webhook configurations in a FileConfig, collecting all validation errors before returning.
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" yaml:"name"`
// URL is the HTTPS endpoint to call.
URL string `json:"url" yaml:"url"`
// Timeout is the maximum time to wait for a webhook response.
Timeout time.Duration `json:"timeout" yaml:"timeout" swaggertype:"primitive,integer"`
// FailurePolicy determines behavior when the webhook call fails.
FailurePolicy FailurePolicy `json:"failure_policy" yaml:"failure_policy"`
// TLSConfig holds optional TLS configuration (CA bundles, client certs).
TLSConfig *TLSConfig `json:"tls_config,omitempty" yaml:"tls_config,omitempty"`
// HMACSecretRef is an optional reference to an HMAC secret for payload signing.
HMACSecretRef string `json:"hmac_secret_ref,omitempty" yaml:"hmac_secret_ref,omitempty"`
}
Config holds the configuration for a single webhook.
func (*Config) UnmarshalJSON ¶ added in v0.19.0
UnmarshalJSON accepts webhook timeout values as either strings (for example "5s") or numeric nanoseconds, while keeping the rest of the struct on the standard JSON path.
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 FileConfig ¶ added in v0.19.0
type FileConfig struct {
// Validating is the list of validating webhook configurations.
Validating []Config `yaml:"validating" json:"validating"`
// Mutating is the list of mutating webhook configurations.
Mutating []Config `yaml:"mutating" json:"mutating"`
}
FileConfig is the top-level structure for a webhook configuration file. It supports both YAML and JSON formats.
Example YAML:
validating:
- name: policy-check
url: https://policy.example.com/validate
timeout: 5s
failure_policy: fail
mutating:
- name: hr-enrichment
url: https://hr-api.example.com/enrich
timeout: 3s
failure_policy: ignore
func LoadConfig ¶ added in v0.19.0
func LoadConfig(path string) (*FileConfig, error)
LoadConfig reads and parses a webhook configuration file. The format is auto-detected by file extension: ".json" uses JSON decoding; all other extensions (including ".yaml" and ".yml") use YAML decoding.
func MergeConfigs ¶ added in v0.19.0
func MergeConfigs(configs ...*FileConfig) *FileConfig
MergeConfigs merges multiple FileConfigs into one. Webhooks with the same name are de-duplicated: entries from later configs override entries from earlier ones (last-writer-wins per webhook name). The resulting Validating and Mutating slices preserve the order in which unique names were first seen and apply overrides in place.
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" yaml:"ca_bundle_path,omitempty"`
// ClientCertPath is the path to a client certificate for mTLS.
ClientCertPath string `json:"client_cert_path,omitempty" yaml:"client_cert_path,omitempty"`
// ClientKeyPath is the path to a client key for mTLS.
ClientKeyPath string `json:"client_key_path,omitempty" yaml:"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" yaml:"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.
Directories
¶
| Path | Synopsis |
|---|---|
|
Package mutating implements a mutating webhook middleware for ToolHive.
|
Package mutating implements a mutating webhook middleware for ToolHive. |
|
Package validating implements a validating webhook middleware for ToolHive.
|
Package validating implements a validating webhook middleware for ToolHive. |