ous callbacks. The following steps outline the production-ready implementation.
1. Gateway Configuration
The webhook adapter runs an Axum HTTP server. You must configure the adapter with a shared secret for HMAC verification and define the allowed callback domains. Garudust enforces a network guard that blocks callbacks to private IP ranges (e.g., 192.168.x.x, 10.x.x.x, localhost) to prevent SSRF attacks.
# garudust-config.yaml
webhook_adapter:
enabled: true
listen_port: 8080
endpoint_path: "/invoke"
hmac_secret: "${GARUDUST_WEBHOOK_SECRET}"
allowed_callback_domains:
- "ops.internal.example.com"
- "ci.example.com"
2. Invocation Payload Structure
External systems send a JSON payload to the configured endpoint. The payload must include the task instruction and a response URI. Optional fields allow for role-based access control and conversation continuity.
Interface Definition:
interface GarudustTrigger {
/** The task description passed to agent.run() */
directive: string;
/** URL where Garudust POSTs the agent's response */
response_uri: string;
/** Optional: Identity for RBAC checks */
principal?: string;
/** Optional: Key to pin conversation history */
conversation_anchor?: string;
}
Example Invocation:
const triggerPayload: GarudustTrigger = {
directive: "Analyze the attached log file for OOM errors and suggest remediation steps.",
response_uri: "https://ops.internal.example.com/hooks/garudust/callback",
principal: "devops-service-account",
conversation_anchor: "incident-INC-2024-05-21"
};
3. HMAC Signature Generation
Every request must include a valid HMAC-SHA256 signature. The signature is computed over the raw request body using the shared secret. Garudust rejects requests with invalid or missing signatures with a 401 Unauthorized response.
import crypto from 'crypto';
function signRequest(body: string, secret: string): string {
const hmac = crypto.createHmac('sha256', secret);
hmac.update(body);
return `sha256=${hmac.digest('hex')}`;
}
// Usage in event source
const rawBody = JSON.stringify(triggerPayload);
const signature = signRequest(rawBody, process.env.GARUDUST_SECRET);
await fetch('https://garudust.internal.example.com/invoke', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Invocation-Signature': signature
},
body: rawBody
});
4. Asynchronous Callback Handling
The webhook endpoint returns 202 Accepted immediately. The agent executes asynchronously. Upon completion, Garudust POSTs the result to the response_uri.
Callback Response Structure:
interface GarudustCallback {
result: string;
status: 'completed' | 'failed';
conversation_anchor?: string;
metadata?: Record<string, unknown>;
}
Callback Handler Implementation:
import express from 'express';
const app = express();
app.use(express.json());
app.post('/hooks/garudust/callback', (req, res) => {
const payload: GarudustCallback = req.body;
// Verify signature (omitted for brevity, same logic as invocation)
if (payload.status === 'completed') {
console.log(`Agent completed task: ${payload.result}`);
// Route result to appropriate system based on conversation_anchor
routeToSystem(payload.conversation_anchor, payload.result);
} else {
console.error(`Agent task failed: ${payload.result}`);
// Trigger alerting or retry logic
}
res.status(200).send('OK');
});
5. Conversation Continuity
The conversation_anchor field enables stateful interactions across multiple triggers. When provided, all invocations with the same anchor share the same conversation history. This is critical for workflows that require context accumulation, such as multi-step code reviews or incident investigation.
- Omitting
conversation_anchor: Results in isolated sessions keyed to the response_uri. Each trigger starts with a fresh context.
- Using
conversation_anchor: Pins the session. Subsequent triggers with the same anchor append to the existing history.
Strategy: Use unique anchors per entity (e.g., pr-{number}, ticket-{id}) to maintain context within a specific workflow while isolating unrelated tasks.
Pitfall Guide
Production deployments of the webhook protocol often encounter recurring issues. The following pitfalls highlight common mistakes and their remedies.
-
Blocking on the 202 Response
- Explanation: Developers sometimes treat the
202 Accepted response as the agent's result, leading to incorrect logic or timeouts.
- Fix: Treat the
202 as an acknowledgment only. All result processing must occur in the callback handler. Implement idempotent callback endpoints to handle retries safely.
-
Ignoring HMAC Verification
- Explanation: Failing to verify signatures on incoming callbacks allows spoofed responses to inject malicious data into your system.
- Fix: Always verify the
X-Invocation-Signature header on both the invocation request and the callback response. Use constant-time comparison to prevent timing attacks.
-
Private IP Callback URLs
- Explanation: Garudust's network guard blocks callbacks to private IP ranges to prevent SSRF. Using
localhost or internal IPs in response_uri will cause callback failures.
- Fix: Ensure
response_uri points to a publicly routable endpoint or a service accessible via a reverse proxy with a valid domain. Use DNS names instead of IP addresses.
-
Session Key Collisions
- Explanation: Reusing
conversation_anchor values across unrelated tasks causes context leakage, where the agent mixes information from different workflows.
- Fix: Generate unique anchors per workflow instance. Incorporate timestamps or UUIDs if necessary (e.g.,
incident-{id}-{timestamp}). Audit anchor usage to ensure isolation.
-
Overloading the Directive
- Explanation: Packing excessive context or instructions into the
directive field can exceed token limits or confuse the agent's reasoning.
- Fix: Keep the directive concise and actionable. Rely on the agent's tools to fetch additional context. Use structured data in the callback to provide supplementary information if needed.
-
Lack of Retry Logic on Callbacks
- Explanation: Network issues or temporary service outages can cause callback delivery failures. Without retries, results may be lost.
- Fix: Implement retry logic in the callback handler or use a message queue to buffer results. Ensure the handler is idempotent to process duplicate deliveries safely.
-
Assuming Pull Capability
- Explanation: The webhook adapter is a push target. It cannot poll external sources or watch for events autonomously.
- Fix: Use external schedulers or event sources to trigger the webhook. For pull-based use cases, implement a cron job or watcher that invokes the webhook when conditions are met.
Production Bundle
Action Checklist
Decision Matrix
| Scenario | Recommended Approach | Why | Cost Impact |
|---|
| High-Volume Event Stream | Webhook Adapter | Decouples latency; scales with event throughput. | Low (Async processing) |
| Scheduled Data Sync | Cron + Webhook | External scheduler triggers webhook; agent processes batch. | Medium (Scheduler overhead) |
| Interactive Chat | Direct API | Low latency; synchronous response needed. | High (Session management) |
| Internal Monitoring | Webhook + Queue | Queue buffers callbacks; ensures reliability. | Medium (Queue costs) |
Configuration Template
# Production-ready Garudust Webhook Configuration
webhook_adapter:
enabled: true
listen_port: 8080
endpoint_path: "/invoke"
hmac_secret: "${GARUDUST_WEBHOOK_SECRET}"
allowed_callback_domains:
- "ops.internal.example.com"
- "ci.example.com"
rate_limit:
requests_per_second: 100
burst_size: 200
logging:
level: "info"
format: "json"
Quick Start Guide
- Set Environment Variables: Export
GARUDUST_WEBHOOK_SECRET with a secure random string.
- Start Garudust: Launch the service with the webhook adapter enabled.
- Send Test Invocation: Use
curl or a script to POST a payload to /invoke with a valid HMAC signature.
- Verify Callback: Check that your callback endpoint receives the agent's response asynchronously.
- Integrate Event Source: Connect your event source (e.g., GitHub, email processor) to trigger the webhook on relevant events.