src — webhooks
src — webhooks
The src/webhooks module provides a robust system for managing and processing incoming webhooks. Its core component, the WebhookManager class, handles everything from configuration persistence to payload validation and event dispatching.
Module Purpose
The primary purpose of the src/webhooks module is to:
- Manage Webhook Configurations: Allow for the registration, retrieval, modification, and removal of webhook definitions, including their unique IDs, names, optional secrets, and associated agent messages.
- Process Incoming Payloads: Validate and process data received from external webhook sources, including optional signature verification for security.
- Resolve Agent Messages: Dynamically generate agent messages by injecting data from the incoming webhook payload into predefined templates.
- Dispatch Events: Notify internal application components when a webhook payload has been successfully processed, enabling reactive logic.
- Persist Configuration: Store webhook configurations to disk, ensuring they are maintained across application restarts.
Key Components
WebhookConfig Interface
Defines the structure for a single webhook's configuration:
export interface WebhookConfig {
id: string; class="hl-cmt">// Unique identifier for the webhook
name: string; class="hl-cmt">// User-friendly name
secret?: string; class="hl-cmt">// Optional secret for signature verification
agentMessage: string; class="hl-cmt">// Template string for the agent39;s response
enabled: boolean; class="hl-cmt">// Whether the webhook is active
createdAt: number; class="hl-cmt">// Timestamp of creation
}
WebhookPayloadCallback Type
Defines the signature for functions that listen to processed webhook payloads:
export type WebhookPayloadCallback = (webhookId: string, body: Record<string, unknown>) => void;
WebhookManager Class
The central class responsible for all webhook operations.
Constructor
constructor(configDir?: string)
Initializes the WebhookManager. It sets the path for webhooks.json (defaulting to .codebuddy/webhooks.json) and immediately attempts to load any existing webhook configurations from this file.
Configuration Management
register(name: string, agentMessage: string, secret?: string): WebhookConfig- Creates a new webhook with a unique ID, saves it to disk, and returns its configuration.
remove(id: string): boolean- Deletes a webhook by its ID. Returns
trueif deleted,falseotherwise. Triggers a save. setEnabled(id: string, enabled: boolean): boolean- Toggles the
enabledstatus of a webhook. Returnstrueif updated,falseif not found. Triggers a save. get(id: string): WebhookConfig | undefined- Retrieves a single webhook configuration by its ID.
list(): WebhookConfig[]- Returns an array of all registered webhook configurations.
Payload Processing
processPayload(id: string, body: Record
This is the primary entry point for handling incoming webhook data.
- Lookup & Status Check: Retrieves the
WebhookConfigbyid. Returns an error if not found or disabled. - Signature Verification: If the webhook has a
secretconfigured, it expects asignaturein the request. It usesprivate verifySignatureto cryptographically validate the payload against the secret. An invalid or missing signature results in an error. - Message Resolution: Calls
private resolveTemplateto generate the final agent message by replacing placeholders inagentMessagewith values from thebody. - Event Dispatch: Notifies all registered
WebhookPayloadCallbacklisteners (both specific to the webhookidand global*listeners) with the webhook ID and the processedbody. Listener errors are silently ignored. - Response: Returns either the resolved
messageor anerrorobject.
##### private verifySignature(payload: string, signature: string, secret: string): boolean
A utility method that uses Node.js's crypto module (createHmac, timingSafeEqual) to securely verify an HMAC SHA256 signature. It's crucial for ensuring the integrity and authenticity of incoming webhook payloads when a secret is configured.
##### private resolveTemplate(template: string, body: Record
Parses the agentMessage template string, replacing {{body.path.to.value}} placeholders with corresponding values from the body object. It includes basic protection against prototype pollution paths (__proto__, constructor, prototype). If a path is not found or invalid, the placeholder is left as is.
Event Subscription
onPayload(webhookId: string, callback: WebhookPayloadCallback): () => void
Allows other parts of the application to subscribe to webhook payload events.
webhookId: The ID of the specific webhook to listen to, or'*'to listen to all webhooks.callback: The function to be invoked when a payload is processed.- Returns an unsubscribe function, which can be called to remove the listener.
Persistence
private load(): void- Reads
webhooks.jsonfrom the configuredconfigPath. If the file exists and contains valid JSON, it populates thewebhooksmap. Errors during loading are caught, resulting in an empty webhook state. private save(): void- Writes the current state of all webhooks to
webhooks.json. It ensures the directory exists before writing. Errors during saving are silently ignored.
Execution Flow: Processing a Webhook Payload
The following diagram illustrates the typical flow when an external webhook request is received and processed by the WebhookManager:
graph TD
A[External Webhook Request] --> B{WebhookManager.processPayload(id, body, signature?)};
B --> C{Get WebhookConfig by ID};
C -- Not Found/Disabled --> D[Return { error: ... }];
C -- Found & Enabled --> E{WebhookConfig has 'secret'?};
E -- Yes --> F{Call verifySignature(payload, signature, secret)};
F -- Invalid Signature --> D;
F -- Valid Signature --> G{Call resolveTemplate(agentMessage, body)};
E -- No Secret --> G;
G --> H{Notify onPayload Listeners (specific & global)};
H --> I[Return { message: resolvedAgentMessage }];
Integration with the Codebase
Incoming Calls
src/server/index.ts(viacreateApp): This is the primary integration point. The HTTP server routes incoming webhook requests to theWebhookManager.processPayloadmethod, making it the public API for external webhook providers.tests/webhooks/webhook-manager.test.ts: The unit tests extensively interact with all public methods ofWebhookManagerto ensure correct functionality.
Outgoing Calls
cryptomodule: Used byverifySignaturefor secure hashing and comparison.fsmodule: Used byloadandsavefor reading and writing webhook configurations to disk.pathmodule: Used by the constructor andsavefor resolving and creating the configuration file path.- Internal Callbacks:
processPayloadinvokesWebhookPayloadCallbackfunctions registered viaonPayload. This allows other internal modules (e.g.,tests/features/tailscale-dashboard-nodes.test.tsin a test scenario) to react to processed webhook events.