# How the Integration Broker's Loopback Secret Broker Works with Per-Worker Capability Tokens

> Understand how Munder Difflin's loopback secret broker uses per worker capability tokens to securely enable integration calls without exposing secrets. Learn about this zero-knowledge bridge.

- Repository: [Chaitanya Giri/munder-difflin](https://github.com/chaitanyagiri/munder-difflin)
- Tags: internals
- Published: 2026-08-29

---

**The loopback secret broker in Munder Difflin acts as a zero-knowledge bridge by binding exclusively to 127.0.0.1 and issuing ephemeral per-worker capability tokens that allow workers to invoke specific integrations without ever handling the underlying secrets.**

The integration broker's loopback secret broker in the `chaitanyagiri/munder-difflin` repository provides cryptographic isolation between ephemeral worker processes and sensitive third-party credentials. Implemented in [`src/main/integrationBroker.ts`](https://github.com/chaitanyagiri/munder-difflin/blob/main/src/main/integrationBroker.ts), this lightweight HTTP server mediates all outbound integration traffic by validating opaque capability tokens and injecting real secrets only at the broker layer, ensuring workers operate with a strict need-to-know boundary.

## Architecture Overview

The broker exposes a local-only HTTP endpoint that workers use as a proxy for all integration calls. When initialized via `start()` at `src/main/integrationBroker.ts:87-102`, the server binds to an OS-assigned port on the **127.0.0.1** interface, rendering it unreachable from external network interfaces. Workers construct request URLs using the pattern:

```

http://127.0.0.1:<port>/i/<integrationId>/<path>

```

Rather than authenticating with real integration secrets, workers present a **per-worker capability token**—a random 32-byte base64url opaque handle—in either the `Authorization: Bearer` header or the custom `X-MD-Broker-Token` header. The broker validates this token against an in-memory capability map, verifies the worker is authorized for the requested integration ID, decrypts the real secret internally, and forwards the sanitized request upstream.

## Per-Worker Capability Token Lifecycle

### Minting Tokens via grant()

When the main process spawns a worker, it calls `grant(workerId, allowedIds)` at `src/main/integrationBroker.ts:26-35` to issue a new capability token. This method first revokes any existing token for the same worker ID to prevent token accumulation, then generates a cryptographically random 32-byte value encoded as base64url. The broker maintains two maps:

- **worker → token**: Tracks the current token for each worker
- **token → capability**: Stores the allowed integration IDs and grant timestamp

The returned token is injected into the worker's environment variables (typically `MD_BROKER_TOKEN`), while the broker URL is provided via `MD_BROKER_URL`.

### Revoking Access via revoke()

Upon worker termination, the main process invokes `revoke(workerId)` at `src/main/integrationBroker.ts:37-41`. This removes the worker's token from both internal maps, ensuring that any in-flight or subsequent requests using that token will fail authentication. The cleanup is synchronous and absolute, preventing stale tokens from persisting in memory after worker exit.

## Security Mechanisms and Isolation

### Loopback Interface Enforcement

Before processing any request, the broker executes `isLoopback()` at `src/main/integrationBroker.ts:68-73` to verify the connection originates from the local machine. The guard rejects any socket address that does not match `127.*` IPv4 ranges or the IPv6 loopback `::1`. This ensures that even if the port were exposed through firewall misconfiguration, remote hosts could not reach the secret broker.

### Timing-Safe Token Validation

To prevent timing side-channel attacks, the broker implements `resolveCapability()` at `src/main/integrationBroker.ts:44-51` using `timingSafeEqual` for token comparison. Rather than using standard string equality that short-circuits on mismatch, the function compares the presented token against stored entries in constant time, preventing attackers from inferring valid token prefixes through response timing analysis.

### Integration Scope Authorization

After validating the token's authenticity, the broker checks authorization at `src/main/integrationBroker.ts:89-92` by verifying that the requested `<integrationId>` exists in the capability's `allowedIds` array. This creates fine-grained per-worker access control: a worker granted capabilities for `['slack', 'github']` cannot access the `linear` integration even with a valid token.

## Request Flow and Secret Injection

### Token Extraction

Incoming requests trigger `tokenFrom()` at `src/main/integrationBroker.ts:60-68`, which extracts the capability token from either:
- The `Authorization` header with `Bearer ` prefix
- The custom `X-MD-Broker-Token` header

If neither header is present, the broker immediately returns a 401 response.

### Secret Resolution and Upstream Forwarding

Once authorized, the broker resolves the integration record via `getRecord` and decrypts the secret using `getSecret` at `src/main/integrationBroker.ts:94-106`. This decryption occurs exclusively inside the broker process; the worker never observes the real credential.

The `forward()` implementation at `src/main/integrationBroker.ts:112-140` then:
1. Sanitizes inbound headers by stripping `authorization`, `host`, and other hop-by-hop headers
2. Constructs upstream authentication headers using `buildAuthHeaders` with the decrypted secret
3. Streams the request body to the integration endpoint
4. Scrubs sensitive response headers before returning the payload to the worker

This pipeline creates a **zero-knowledge bridge**: workers invoke integrations by path and ID alone, while the broker handles all cryptographic material.

## Implementation Examples

### Starting the Broker and Granting Capabilities

```typescript
import { IntegrationBroker } from '@/main/integrationBroker';
import { getIntegrationRecord, decryptSecret } from '@/shared/integrations';

// Initialize with secret resolution dependencies
const broker = new IntegrationBroker({
  getRecord: getIntegrationRecord,
  getSecret: decryptSecret,
});

// Start loopback listener on OS-assigned port
await broker.start();  // Returns { ok: true, port: 53741 }
const brokerUrl = broker.url();  // "http://127.0.0.1:53741"

// Grant worker-42 access only to Slack integration
const workerId = 'worker-42';
const token = broker.grant(workerId, ['slack']);
// Token is passed to worker via environment variables

```

### Worker Request Pattern

```typescript
import fetch from 'node-fetch';

// Environment injected by main process
const brokerUrl = process.env.MD_BROKER_URL;   // http://127.0.0.1:53741
const token = process.env.MD_BROKER_TOKEN;     // opaque capability token

// Worker invokes Slack without knowing the real Slack token
await fetch(`${brokerUrl}/i/slack/postMessage`, {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${token}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({ channel: '#general', text: 'Hello' }),
});

```

### Token Revocation on Cleanup

```typescript
// Main process cleanup when worker exits
broker.revoke('worker-42');
// Subsequent requests with this token now receive HTTP 401

```

## Summary

- The **loopback secret broker** binds exclusively to 127.0.0.1, ensuring only local processes can access the integration proxy.
- **Per-worker capability tokens** are ephemeral 32-byte random handles that map to specific integration whitelists, not actual secrets.
- The `grant()` and `revoke()` lifecycle methods provide fine-grained, time-bound access control for worker processes.
- **Timing-safe comparison** via `timingSafeEqual` prevents side-channel attacks during token validation.
- Real secrets are decrypted and injected only inside the broker's `forward()` method, maintaining a zero-knowledge architecture for workers.

## Frequently Asked Questions

### What happens if a worker tries to reuse a revoked capability token?

The broker returns an HTTP 401 Unauthorized response. When `revoke(workerId)` is called at `src/main/integrationBroker.ts:37-41`, the token is immediately deleted from both the worker-to-token and token-to-capability maps. Any subsequent request bearing that token fails the `resolveCapability()` lookup, rejecting the request before any secret material is accessed.

### How does the broker enforce which integrations a worker can access?

During request processing at `src/main/integrationBroker.ts:89-92`, the broker compares the `<integrationId>` path segment against the `allowedIds` array stored in the capability map. If the integration ID is not present in the worker's granted capabilities, the broker rejects the request with a 403 Forbidden status, ensuring workers cannot pivot to unauthorized services even if they possess a valid token.

### Why does the broker validate tokens using timingSafeEqual?

The `resolveCapability()` method at `src/main/integrationBroker.ts:44-51` uses Node.js's `timingSafeEqual` to compare the presented token against stored entries in constant time. Standard string comparison operators short-circuit on the first mismatched character, potentially leaking information about valid token prefixes through timing analysis. Constant-time comparison eliminates this side channel.

### Can the broker be configured to listen on interfaces other than loopback?

No. The `isLoopback()` guard at `src/main/integrationBroker.ts:68-73` explicitly rejects any connection where `socket.remoteAddress` is not within the `127.0.0.0/8` range or the IPv6 loopback `::1`. This hardening measure ensures that integration secrets remain inaccessible to network attackers even if the host firewall is misconfigured to expose the broker port.