# How to Integrate Munder-Difflin with External Services: A Complete Developer Guide

> Learn how to integrate Munder-Difflin with external services using its secure phase-2 integrations layer. This guide details the secret store, registry, and broker for reliable API routing.

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

---

**Munder-Difflin integrates with external services through a phase-2 integrations layer that uses an encrypted secret store, integration registry, and loopback broker to securely route HTTP requests from workers to any REST API.**

The **munder-difflin** framework provides a secure, three-component architecture for connecting AI agents to third-party APIs. According to the chaitanyagiri/munder-difflin source code, agents call external services by routing requests through a local broker that handles authentication transparently—workers never handle raw credentials directly.

## How the Munder-Difflin Integration Architecture Works

The integration system consists of three tightly-coupled components designed to separate configuration, secrets, and request execution.

### Component Overview

| Component | Responsibility | Key File |
|---|---|---|
| **Integration Registry** | Stores metadata (ID, label, kind, base URL, auth type) without secrets | [`src/shared/integrations.ts`](https://github.com/chaitanyagiri/munder-difflin/blob/main/src/shared/integrations.ts) |
| **Encrypted Secret Store** | Persists credentials encrypted with Electron's `safeStorage` | [`src/main/integrations.ts`](https://github.com/chaitanyagiri/munder-difflin/blob/main/src/main/integrations.ts) |
| **Loopback Integration Broker** | Resolves URLs, injects auth headers, and forwards requests | [`src/main/integrationBroker.ts`](https://github.com/chaitanyagiri/munder-difflin/blob/main/src/main/integrationBroker.ts) |

This separation ensures that **secrets are never exposed to the renderer process or logged**, while still allowing workers to make authenticated API calls seamlessly.

## Registering a New Integration

You can register integrations either through the Settings UI or programmatically via the preload bridge. The UI presents built-in templates defined in [`src/shared/integrations.ts`](https://github.com/chaitanyagiri/munder-difflin/blob/main/src/shared/integrations.ts) including GitHub, Linear, Stripe, and custom REST endpoints.

### Programmatic Registration Example

```typescript
// List built-in templates
const templates = await window.cth.integrationsTemplates();

// Create a custom REST integration for a fictional "Acme API"
const newRecord = {
  id: 'acme',
  label: 'Acme API',
  kind: 'custom-rest',
  baseUrl: 'https://api.acme.com',
  authType: 'header',
  authHeader: 'X-Api-Key',
  enabled: true
};

const up = await window.cth.integrationsUpsert(newRecord);
if (!up.ok) throw new Error(up.error);

// Store the secret (never exposed to the renderer)
await window.cth.integrationsSetSecret({ id: 'acme', secret: 'my-super-secret-key' });

```

The `integrationsUpsert` call validates against the `IntegrationRecord` schema defined in [`src/shared/integrations.ts`](https://github.com/chaitanyagiri/munder-difflin/blob/main/src/shared/integrations.ts). The `authType` property determines which authentication mechanism the broker will use when building request headers.

## Securing Credentials with the Encrypted Store

Secrets are handled exclusively in the main process through four operations implemented in [`src/main/integrations.ts`](https://github.com/chaitanyagiri/munder-difflin/blob/main/src/main/integrations.ts):

- `setSecret` — encrypts and stores credentials using `safeStorage.encryptString`
- `getSecret` — decrypts credentials only when needed for request forwarding
- `hasSecret` — checks for credential existence without decryption
- `deleteSecret` — removes credentials permanently

**Critical security property:** Secrets are decrypted only in [`src/main/integrations.ts`](https://github.com/chaitanyagiri/munder-difflin/blob/main/src/main/integrations.ts) and never transmitted to the renderer or exposed in logs. The `authHeader` field in the registry record specifies which header receives the decrypted secret value.

## Making Authenticated Requests from Workers

Once an integration is enabled, workers invoke it through a local loopback URL pattern. The broker in [`src/main/integrationBroker.ts`](https://github.com/chaitanyagiri/munder-difflin/blob/main/src/main/integrationBroker.ts) handles all authentication transparently.

### Worker-Side Request Pattern

```javascript
// In a worker script (e.g., a Claude Code skill)
async function fetchAcmeData() {
  // The harness resolves /i/acme/... and injects X-Api-Key automatically
  const resp = await fetch('http://localhost:3000/i/acme/v1/orders?status=open');
  const data = await resp.json();
  return data;
}

```

When the broker receives this request, it executes the following sequence from [`src/main/integrationBroker.ts`](https://github.com/chaitanyagiri/munder-difflin/blob/main/src/main/integrationBroker.ts) and [`src/shared/integrations.ts`](https://github.com/chaitanyagiri/munder-difflin/blob/main/src/shared/integrations.ts):

1. **Lookup** — calls `getRecord('acme')` to obtain `baseUrl`, `authType`, and `authHeader`
2. **URL resolution** — `resolveUpstreamUrl` safely combines base URL with the supplied path, preventing path traversal and host switching attacks
3. **Secret retrieval** — `getSecret` decrypts the credential in the main process only
4. **Header construction** — `buildAuthHeaders` generates the appropriate authentication headers
5. **Request forwarding** — performs the external HTTP call and returns the response to the worker

## Managing and Removing Integrations

Disable integrations by setting `enabled: false` in the record, or remove them entirely to delete associated secrets.

```typescript
// Disable without deleting secrets
await window.cth.integrationsUpsert({ ...existingRecord, enabled: false });

// Complete removal (also deletes stored secret)
await window.cth.integrationsRemove({ id: 'acme' });

```

The `integrationsRemove` call triggers cascading cleanup: the registry record is deleted from the config, and `deleteSecret` purges the encrypted credential from the secret store.

## Key Implementation Files for Integration Development

| File | Purpose |
|---|---|
| [`src/shared/integrations.ts`](https://github.com/chaitanyagiri/munder-difflin/blob/main/src/shared/integrations.ts) | `IntegrationRecord` schema, validation, `resolveUpstreamUrl`, `buildAuthHeaders`, `INTEGRATION_TEMPLATES` |
| [`src/main/integrations.ts`](https://github.com/chaitanyagiri/munder-difflin/blob/main/src/main/integrations.ts) | Registry CRUD, encrypted `setSecret`/`getSecret`/`hasSecret`/`deleteSecret` |
| [`src/preload/index.ts`](https://github.com/chaitanyagiri/munder-difflin/blob/main/src/preload/index.ts) | IPC bridge exposing `integrationsList`, `integrationsUpsert`, `integrationsSetSecret` to renderer |
| [`src/main/integrationBroker.ts`](https://github.com/chaitanyagiri/munder-difflin/blob/main/src/main/integrationBroker.ts) | Capability injection, HTTP request forwarding, main process broker |

## Summary

- **Three-layer architecture** separates registry metadata, encrypted secrets, and request execution
- **Programmatic control** via `window.cth.integrations*` methods in the preload bridge
- **Transparent authentication** — workers use simple `/i/<id>/<path>` URLs without handling credentials
- **Security guarantees** implemented in [`src/main/integrations.ts`](https://github.com/chaitanyagiri/munder-difflin/blob/main/src/main/integrations.ts): encryption at rest, decryption only in main process, no renderer exposure
- **Built-in templates** for common services (GitHub, Linear, Stripe) plus extensible custom REST support

## Frequently Asked Questions

### How does munder-difflin keep API secrets secure?

Secrets are encrypted using Electron's `safeStorage` module in [`src/main/integrations.ts`](https://github.com/chaitanyagiri/munder-difflin/blob/main/src/main/integrations.ts). They are decrypted only when the broker needs to forward a request, and never exposed to renderer processes, worker threads, or log files. The `setSecret` and `getSecret` functions handle all cryptographic operations in the main process.

### Can workers specify arbitrary URLs when calling integrations?

No. The `resolveUpstreamUrl` function in [`src/shared/integrations.ts`](https://github.com/chaitanyagiri/munder-difflin/blob/main/src/shared/integrations.ts) constrains request URLs to the `baseUrl` registered in the integration record. This prevents path traversal attacks and ensures workers cannot switch to unauthorized hosts by manipulating the path parameter.

### What authentication methods are supported?

The `authType` field in `IntegrationRecord` supports multiple schemes. The `buildAuthHeaders` helper generates appropriate headers based on `authType` and `authHeader` values. Common patterns include Bearer tokens, API keys in custom headers, and Basic authentication—all implemented without exposing raw credentials to workers.

### How do I add support for a new service like Jira or Asana?

Define a new template in `INTEGRATION_TEMPLATES` within [`src/shared/integrations.ts`](https://github.com/chaitanyagiri/munder-difflin/blob/main/src/shared/integrations.ts), then register an integration with `kind` matching your template. The existing broker architecture handles arbitrary REST services without modification, provided you specify the correct `baseUrl`, `authType`, and `authHeader` fields.