@bernierllc/email-mitm-masking
v1.2.0
Published
Email masking and man-in-the-middle routing service for privacy-focused email communication
Readme
@bernierllc/email-mitm-masking
Email masking and man-in-the-middle routing service for privacy-focused email communication. Provides proxy email addresses that mask real user addresses while routing messages bidirectionally with full audit trail and lifecycle management.
Installation
npm install @bernierllc/email-mitm-maskingFeatures
- 🔒 Privacy-First: Generate proxy email addresses to mask real user addresses
- ↔️ Bidirectional Routing: Route emails both inbound (external → real) and outbound (real → external)
- 🔄 Lifecycle Management: Create, activate, deactivate, expire, and revoke proxies
- 📊 Audit Trail: Complete routing audit log for compliance and debugging
- 🎯 Flexible Strategies: Support for per-user, per-contact, and per-conversation masking
- ⏱️ TTL Support: Automatic expiration with configurable time-to-live
- 📈 Usage Tracking: Monitor proxy usage and routing statistics
- 🔒 Quota Enforcement: Prevent abuse with per-user proxy limits
Usage
Basic Setup
import { EmailMaskingService } from '@bernierllc/email-mitm-masking';
const service = new EmailMaskingService({
proxyDomain: 'proxy.example.com',
database: {
host: 'localhost',
port: 5432,
database: 'email_masking',
user: 'postgres',
password: process.env.DB_PASSWORD
},
defaultTTL: 365, // 365 days
maxProxiesPerUser: 100, // 100 proxies per user
auditEnabled: true, // Enable audit logging
strategy: 'per-contact' // Create one proxy per contact
});
await service.initialize();Create a Proxy Email Address
// Create a basic proxy
const proxy = await service.createProxy(
'user123',
'[email protected]'
);
console.log(proxy.proxyEmail);
// Output: [email protected]
// Create proxy with custom TTL
const tempProxy = await service.createProxy(
'user123',
'[email protected]',
{
ttlDays: 30, // Expires in 30 days
externalEmail: '[email protected]',
metadata: { source: 'signup-form', priority: 'high' }
}
);Route Inbound Email (Webhook Handler)
import express from 'express';
const app = express();
app.post('/webhooks/inbound-email', express.text({ type: '*/*' }), async (req, res) => {
const { from, to, subject, text, html } = parseEmail(req.body);
const result = await service.routeInbound(from, to, subject, text, html);
if (result.success) {
res.json({
status: 'routed',
to: result.routedTo,
auditId: result.auditId
});
} else {
res.status(400).json({
status: 'failed',
error: result.error
});
}
});Route Outbound Email (Reply via Proxy)
async function sendReplyViaProxy(
userId: string,
userEmail: string,
recipientEmail: string,
message: string
) {
const result = await service.routeOutbound(
userId,
userEmail,
recipientEmail,
{
subject: 'Re: Your inquiry',
text: message,
html: `<p>${message}</p>`
}
);
if (result.success) {
console.log(`Reply sent via proxy: ${result.proxyUsed}`);
} else {
console.error(`Failed to send reply: ${result.error}`);
}
return result;
}Proxy Lifecycle Management
// Create proxy with 30-day expiration
const proxy = await service.createProxy('user456', '[email protected]', {
ttlDays: 30
});
// Use the proxy for routing...
await service.routeInbound(from, proxy.proxyEmail, subject);
// Deactivate when temporarily not needed
await service.deactivateProxy(proxy.id);
// Permanently revoke
await service.revokeProxy(proxy.id);
// Retrieve proxy details
const proxyDetails = await service.getProxyById(proxy.id);
console.log(proxyDetails.status); // 'revoked'API Reference
EmailMaskingService
new EmailMaskingService(config)
Create a new email masking service instance.
Parameters:
config.proxyDomain(string, required): Domain for proxy addressesconfig.database(DatabaseConfig, required): PostgreSQL connection configconfig.defaultTTL(number, optional): Default TTL in days (0 = unlimited, default: 0)config.maxProxiesPerUser(number, optional): Max proxies per user (0 = unlimited, default: 0)config.auditEnabled(boolean, optional): Enable audit logging (default: true)config.strategy('per-user' | 'per-contact' | 'per-conversation', optional): Masking strategy (default: 'per-user')
initialize(): Promise<void>
Initialize the service, create database schema, and start cleanup jobs.
createProxy(userId, realEmail, options?): Promise<ProxyAddress>
Create a new proxy email address.
Parameters:
userId(string): User identifierrealEmail(string): Real user email addressoptions.ttlDays(number, optional): TTL in daysoptions.externalEmail(string, optional): External contact emailoptions.conversationId(string, optional): Conversation identifieroptions.metadata(object, optional): Custom metadata
Returns: ProxyAddress object
routeInbound(from, to, subject, text?, html?): Promise<RoutingResult>
Route an inbound email from external sender to real address via proxy.
Parameters:
from(string): External sender addressto(string): Proxy email addresssubject(string): Email subjecttext(string, optional): Plain text bodyhtml(string, optional): HTML body
Returns: RoutingResult with routing status and audit ID
routeOutbound(userId, realEmail, externalEmail, emailContent): Promise<RoutingResult>
Route an outbound email from real address to external recipient via proxy.
Parameters:
userId(string): User identifierrealEmail(string): Real user email addressexternalEmail(string): External recipient addressemailContent.subject(string): Email subjectemailContent.text(string, optional): Plain text bodyemailContent.html(string, optional): HTML body
Returns: RoutingResult with routing status and audit ID
deactivateProxy(proxyId): Promise<void>
Deactivate a proxy address (can be reactivated).
revokeProxy(proxyId): Promise<void>
Permanently revoke a proxy address.
getProxyById(proxyId): Promise<ProxyAddress | null>
Retrieve proxy details by ID.
shutdown(): Promise<void>
Cleanup resources and close database connection.
Configuration
Environment Variables
# EMAIL_MITM_MASKING Configuration
EMAIL_MASKING_PROXY_DOMAIN=proxy.example.com
EMAIL_MASKING_DB_HOST=localhost
EMAIL_MASKING_DB_PORT=5432
EMAIL_MASKING_DB_NAME=email_masking
EMAIL_MASKING_DB_USER=postgres
EMAIL_MASKING_DB_PASSWORD=secret
EMAIL_MASKING_DEFAULT_TTL=365 # Days (0 = unlimited)
EMAIL_MASKING_MAX_PROXIES_PER_USER=100 # Max per user (0 = unlimited)
EMAIL_MASKING_AUDIT_ENABLED=true # Enable audit logging
EMAIL_MASKING_STRATEGY=per-contact # per-user, per-contact, per-conversationDatabase Schema
The service automatically creates the following PostgreSQL tables:
proxy_addresses
Stores proxy email address records with lifecycle management.
routing_audit
Audit trail for all routing decisions (when audit is enabled).
masking_rules
User-defined masking rules (reserved for future use).
See the plan file for complete schema definitions.
Security Considerations
- Secure Token Generation: Uses cryptographically secure random tokens for proxy addresses
- Email Validation: Validates proxy domain to prevent spoofing
- Quota Enforcement: Prevents abuse via per-user proxy limits
- Audit Trail: Complete audit log for compliance and debugging
- Status Validation: Checks proxy status before routing (active, expired, revoked)
- SQL Injection Protection: Uses parameterized queries throughout
- Privacy Guarantee: Real email addresses never exposed in external emails
- Automatic Cleanup: Hourly job expires old proxies automatically
Integration Status
- Logger: integrated - Structured logging for all operations
- Docs-Suite: ready - Complete API documentation with TypeDoc
- NeverHub: not-applicable - Service package, NeverHub integration optional
Performance
- Proxy Creation: ~50ms (including database insert)
- Routing Lookup: ~10ms (indexed queries)
- Audit Logging: ~5ms (when enabled)
- Cleanup Job: Runs hourly, processes expired proxies in bulk
Error Handling
All methods return structured results:
interface RoutingResult {
success: boolean;
routedTo?: string;
proxyUsed?: string;
direction: 'inbound' | 'outbound';
error?: string;
auditId?: string;
}Example error handling:
const result = await service.routeInbound(from, to, subject);
if (!result.success) {
switch (result.error) {
case 'Proxy address not found':
// Handle unknown proxy
break;
case 'Proxy is inactive':
// Handle inactive proxy
break;
case 'Proxy expired':
// Handle expired proxy
break;
default:
// Handle other errors
}
}Examples
Per-Contact Masking
const service = new EmailMaskingService({
proxyDomain: 'proxy.example.com',
database: dbConfig,
strategy: 'per-contact'
});
// Each user-contact pair gets a unique proxy
const proxy1 = await service.createProxy('user1', '[email protected]', {
externalEmail: '[email protected]'
});
const proxy2 = await service.createProxy('user1', '[email protected]', {
externalEmail: '[email protected]'
});
// Different proxies for different contacts
console.log(proxy1.proxyEmail !== proxy2.proxyEmail); // trueTemporary Proxies
// Create proxy that expires in 7 days
const tempProxy = await service.createProxy('user123', '[email protected]', {
ttlDays: 7,
metadata: { purpose: 'one-time-signup' }
});
// After 7 days, inbound routing will fail
// Cleanup job will mark it as expiredWebhook Integration (SendGrid)
app.post('/webhooks/sendgrid', express.json(), async (req, res) => {
const { from, to, subject, text, html } = req.body;
const result = await service.routeInbound(from, to, subject, text, html);
res.status(result.success ? 200 : 400).json(result);
});Testing
# Run tests
npm test
# Run tests with coverage
npm run test:coverage
# Run tests once (CI mode)
npm run test:runLicense
Copyright (c) 2025 Bernier LLC
This file is licensed to the client under a limited-use license. The client may use and modify this code only within the scope of the project it was delivered for. Redistribution or use in other products or commercial offerings is not permitted without written consent from Bernier LLC.
Related Packages
Dependencies
- @bernierllc/email-parser - Parse incoming emails (if needed)
- @bernierllc/email-sender - Send routed emails (if needed)
- @bernierllc/crypto-utils - Generate secure proxy tokens (fallback)
- @bernierllc/logger - Structured logging
- @bernierllc/neverhub-adapter - Service discovery (optional)
Part of Suite
- @bernierllc/email-testing-suite - Complete email testing solution
Support
For issues and questions, please refer to the main BernierLLC tools repository.
