How Webhooks and Slack Integration Spawn Ephemeral Workers in Munder Difflin
Munder Difflin uses lightweight HTTP servers (SlackWebhookServer and WebhookServer) to verify incoming requests, buffer payloads, and hand them to the GOD orchestrator, which spawns isolated CLI worker processes that execute tasks and exit immediately upon completion.
Munder Difflin is an open-source automation framework that isolates the security boundary of HTTP endpoints from the execution environment. The system implements ephemeral workers—temporary CLI agents running in isolated worktrees—to process Slack messages and webhook POST requests without maintaining long-lived server-side processes. This architecture ensures that every trigger spawns a fresh worker, executes the requested task, and tears down the process upon completion.
The Entry Points: SlackWebhookServer and WebhookServer
The system exposes two primary ingress points for external triggers. Both servers open temporary public tunnels using tunnelmole so external services can reach local ports, though the HTTP servers remain functional even if tunnel establishment fails.
In src/main/slack.ts, the SlackWebhookServer class handles Slack-specific authentication and event parsing. It listens for incoming events, verifies Slack's signing secrets, and extracts message text by stripping bot mentions.
In src/main/webhook.ts, the WebhookServer class provides a generic, secret-gated HTTP API for user-defined endpoints. It supports multiple configured endpoints, each with independent secrets and JSON Schema validation rules.
Request Verification and Security Boundaries
Before any payload reaches the orchestrator, the servers enforce strict verification to prevent spoofing and replay attacks.
Slack HMAC Verification
For Slack events, the server validates requests using HMAC-SHA256 over the raw request body combined with a replay-window timestamp guard. The verify() function in src/main/slack.ts (lines 86-105) computes the signature using the configured signingSecret and rejects requests outside the allowed time window.
Webhook Secret Validation
For generic webhooks, the verifySecret() function in src/main/webhook.ts (lines 83-91) performs constant-time comparison against endpoint-specific secrets. To prevent endpoint enumeration, unknown webhook IDs are compared against a per-process decoy secret, hiding the existence of valid endpoints from attackers.
Payload Validation and Buffering
Webhook endpoints enforce JSON Schema validation via validateAgainstSchema before processing. Both servers apply strict payload size caps to prevent memory exhaustion. The webhook server stores validated payloads as WebhookDispatch records, while the Slack server creates SlackInboundMessage objects.
Handoff to the GOD Orchestrator
Once verification succeeds, both servers invoke the onMessage callback, passing the buffered payload to the GOD orchestrator rather than processing it within the HTTP server context.
Creating Dispatch Records
The orchestrator receives either a WebhookDispatch or SlackInboundMessage record depending on the source. This separation allows the system to track provenance while maintaining a unified task queue interface.
Spawning Ephemeral Workers
The orchestrator spawns a dedicated worker process—a real CLI agent running in its own isolated worktree—to execute the requested agent (e.g., Claude Code, Antigravity). Unlike traditional serverless architectures that reuse containers, Munder Difflin creates a fresh OS process for every task. The worker executes the agent CLI, performs the work, and exits automatically upon completion.
Result Reporting and Cleanup
After the worker finishes, the orchestrator handles result delivery through source-specific channels before cleaning up resources.
Slack Thread Replies
For Slack-originated tasks, the orchestrator calls postSlackReply() defined in src/main/slack.ts (lines 48-55). This function makes a raw HTTPS POST to Slack's API using the bot token, posting results back into the originating thread without requiring the Slack SDK.
Token-Based Status Polling
For webhook-originated tasks, the system generates a capability token that callers use to poll for status. The WebhookServer exposes a GET endpoint that accepts this token in the x-md-webhook-token header and returns a WebhookTaskStatus object containing the result or current execution state.
Automatic Worker Teardown
Because each task runs as a separate OS process, the worker automatically exits when the agent CLI completes. The orchestrator records task completion in its internal state but maintains no persistent connection to the worker process. This ephemeral design leaves no long-lived server-side processes between requests, ensuring complete isolation between consecutive executions.
Code Examples
Creating a Slack webhook server and starting it:
import { SlackWebhookServer } from './slack';
const slackServer = new SlackWebhookServer({
port: 3100,
signingSecret: process.env.SLACK_SIGNING_SECRET!,
channelId: 'C01ABCDEF', // optional filter
onMessage: async (msg) => {
// Forward the inbound message to the orchestrator (Michael)
await orchestrator.enqueueSlackMessage(msg);
},
});
await slackServer.start(); // opens tunnel and begins listening
console.log('Slack endpoint URL →', slackServer.tunnelUrl);
Creating a generic webhook server with multiple endpoints:
import { WebhookServer, type WebhookEndpoint } from './webhook';
const endpoints: WebhookEndpoint[] = [
{
id: 'github',
name: 'GitHub PR webhook',
secret: process.env.WEBHOOK_GITHUB_SECRET!,
schema: JSON.stringify({
type: 'object',
properties: { message: { type: 'string' }, title: { type: 'string' } },
required: ['message'],
}),
},
];
const webhookServer = new WebhookServer({
port: 3200,
endpoints,
onMessage: (msg, ep) => orchestrator.enqueueWebhookMessage(msg, ep),
lookupStatus: (token) => orchestrator.getTaskStatus(token),
});
await webhookServer.start(); // opens tunnel and begins listening
console.log('Webhook public URL →', webhookServer.publicUrl());
Posting a reply back to Slack from an agent (executed inside a worker):
import { postSlackReply } from './slack';
await postSlackReply({
botToken: process.env.SLACK_BOT_TOKEN!,
channel: 'C01ABCDEF',
thread_ts: '1623456789.123456',
text: '✅ Done processing your request!',
});
Polling a webhook task's status:
import fetch from 'node-fetch';
async function poll(token: string) {
const resp = await fetch(`${webhookServer.publicUrl()}/github`, {
headers: { 'x-md-webhook-token': token },
});
const data = await resp.json();
console.log('Task status:', data);
}
Summary
- Dual Entry Architecture: Munder Difflin operates
SlackWebhookServerandWebhookServeras lightweight HTTP ingress points that use tunnelmole for public exposure while maintaining local availability. - Cryptographic Verification: Slack requests undergo HMAC-SHA256 verification with timestamp guards, while webhooks use constant-time secret comparison against per-endpoint secrets with decoy-based enumeration protection.
- Orchestrator Handoff: Verified payloads transfer to the GOD orchestrator via
onMessagecallbacks, creatingWebhookDispatchorSlackInboundMessagerecords without processing within the HTTP server context. - Process Isolation: Each trigger spawns a fresh CLI worker process in an isolated worktree, executing the requested agent and terminating immediately upon completion.
- Result Delivery: Workers report results back through Slack thread replies (
postSlackReply) or capability-token-based status endpoints (WebhookTaskStatus), with automatic cleanup ensuring no persistent server-side state remains.
Frequently Asked Questions
How does Munder Difflin prevent replay attacks on Slack webhooks?
The verify() function in src/main/slack.ts implements a replay window guard by validating the X-Slack-Request-Timestamp header against the current time, rejecting requests older than five minutes. Combined with HMAC-SHA256 signature verification using the shared signingSecret, this prevents attackers from replaying captured request payloads.
What happens if the tunnelmole tunnel fails to establish?
The servers treat tunnel creation as non-fatal. While the tunnel URL is displayed to the user for convenience, both SlackWebhookServer and WebhookServer continue listening on their configured local ports. This ensures that local testing or reverse proxy configurations remain functional even when public tunnel services are unavailable.
Why does the webhook server use a decoy secret for unknown endpoints?
The verifySecret() function in src/main/webhook.ts performs constant-time comparison against a per-process decoy secret when receiving requests for unknown webhook IDs. This prevents timing-based enumeration attacks that could otherwise reveal which webhook endpoints exist on the server, protecting the attack surface from reconnaissance.
How does the orchestrator know when to clean up ephemeral workers?
Ephemeral workers require no explicit cleanup because they are standard OS processes that exit automatically when the agent CLI completes. The orchestrator tracks task completion status internally for result reporting purposes, but the worker process itself terminates independently, leaving no persistent connection or daemon threads behind.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →