# How Webhooks and Slack Integration Spawn Ephemeral Workers in Munder Difflin

> Discover how Munder Difflin leverages webhooks and Slack integration to dynamically spawn ephemeral workers. Learn about lightweight servers, request verification, and immediate task execution.

- Repository: [Chaitanya Giri/munder-difflin](https://github.com/chaitanyagiri/munder-difflin)
- Tags: how-to-guide
- Published: 2026-08-20

---

**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`](https://github.com/chaitanyagiri/munder-difflin/blob/main/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`](https://github.com/chaitanyagiri/munder-difflin/blob/main/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`](https://github.com/chaitanyagiri/munder-difflin/blob/main/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`](https://github.com/chaitanyagiri/munder-difflin/blob/main/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`](https://github.com/chaitanyagiri/munder-difflin/blob/main/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:*

```typescript
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:*

```typescript
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):*

```typescript
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:*

```typescript
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 `SlackWebhookServer` and `WebhookServer` as 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 `onMessage` callbacks, creating `WebhookDispatch` or `SlackInboundMessage` records 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`](https://github.com/chaitanyagiri/munder-difflin/blob/main/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`](https://github.com/chaitanyagiri/munder-difflin/blob/main/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.