npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@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-masking

Features

  • 🔒 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 addresses
  • config.database (DatabaseConfig, required): PostgreSQL connection config
  • config.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 identifier
  • realEmail (string): Real user email address
  • options.ttlDays (number, optional): TTL in days
  • options.externalEmail (string, optional): External contact email
  • options.conversationId (string, optional): Conversation identifier
  • options.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 address
  • to (string): Proxy email address
  • subject (string): Email subject
  • text (string, optional): Plain text body
  • html (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 identifier
  • realEmail (string): Real user email address
  • externalEmail (string): External recipient address
  • emailContent.subject (string): Email subject
  • emailContent.text (string, optional): Plain text body
  • emailContent.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-conversation

Database 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

  1. Secure Token Generation: Uses cryptographically secure random tokens for proxy addresses
  2. Email Validation: Validates proxy domain to prevent spoofing
  3. Quota Enforcement: Prevents abuse via per-user proxy limits
  4. Audit Trail: Complete audit log for compliance and debugging
  5. Status Validation: Checks proxy status before routing (active, expired, revoked)
  6. SQL Injection Protection: Uses parameterized queries throughout
  7. Privacy Guarantee: Real email addresses never exposed in external emails
  8. 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); // true

Temporary 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 expired

Webhook 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:run

License

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

Part of Suite

Support

For issues and questions, please refer to the main BernierLLC tools repository.