Plugin System

The bridge supports an extensible plugin system for filtering emails before processing.


How Plugins Work

Plugins are executables that communicate via JSON over stdin/stdout.

┌─────────────┐      JSON stdin      ┌────────┐      JSON stdout     ┌─────────────┐
│   Bridge    │ ──────────────────►  │ Plugin │ ──────────────────►  │   Bridge    │
└─────────────┘                      └────────┘                      └─────────────┘

Plugin Input

The bridge sends email data to the plugin:

{
  "type": "inbound",
  "event": {
    "from": "sender@example.com",
    "to": "recipient@example.com",
    "subject": "Hello",
    "body": "Message content",
    "senderPubkey": "abc123...",
    "recipientPubkey": "def456..."
  },
  "receivedAt": 1704067200,
  "sourceType": "smtp",
  "sourceInfo": "127.0.0.1"
}

Plugin Output

The plugin returns a decision:

{
  "id": "abc123...",
  "action": "accept",
  "msg": "Optional message"
}

Actions

Action Description
accept Allow the email to be processed
reject Reject the email (sender may be notified)
shadowReject Silently discard (no notification)

Configuration

Set the plugin path in your environment:

PLUGIN_PATH=/path/to/your/plugin
PLUGIN_TIMEOUT_MS=30000   # optional, how long the plugin may take

A plugin that does not answer in time is given up on. Decide what that should mean for your bridge: a filter that fails open accepts spam, one that fails closed loses mail.


Available Plugins

The repository ships two JavaScript plugins to start from, bridge-plugins/example.js and bridge-plugins/whitelist.js, plus the Dart plugin below.

uid_ovh

A Dart plugin with advanced filtering:

  • NIP-51 Whitelist: Only accept emails from whitelisted pubkeys
  • NIP-85 Trust Score: Filter based on Web of Trust scores

Setup

cd bridge-plugins/uid_ovh
dart pub get
dart compile exe bin/uid_ovh.dart -o build/uid_ovh

Configuration

export PLUGIN_PATH=/path/to/build/uid_ovh

Writing Your Own Plugin

Requirements

  1. Read JSON from stdin
  2. Process the email data
  3. Write JSON response to stdout
  4. Exit with code 0

Example (Node.js)

#!/usr/bin/env node

const readline = require('readline');

const rl = readline.createInterface({
  input: process.stdin,
  output: process.stdout,
  terminal: false
});

let input = '';

rl.on('line', (line) => {
  input += line;
});

rl.on('close', () => {
  const data = JSON.parse(input);

  // Your filtering logic here
  const isSpam = data.event.subject.toLowerCase().includes('viagra');

  const response = {
    id: data.event.senderPubkey,
    action: isSpam ? 'shadowReject' : 'accept',
    msg: isSpam ? 'Spam detected' : 'OK'
  };

  console.log(JSON.stringify(response));
});

Example (Dart)

import 'dart:convert';
import 'dart:io';

void main() async {
  final input = await stdin.transform(utf8.decoder).join();
  final data = jsonDecode(input);

  // Your filtering logic here
  final isSpam = (data['event']['subject'] as String)
      .toLowerCase()
      .contains('viagra');

  final response = {
    'id': data['event']['senderPubkey'],
    'action': isSpam ? 'shadowReject' : 'accept',
    'msg': isSpam ? 'Spam detected' : 'OK',
  };

  print(jsonEncode(response));
}

Web of Trust Filtering

The uid_ovh plugin supports NIP-85 trust scores:

  1. Looks up sender's trust score on the network
  2. Checks if sender is in recipient's NIP-51 whitelist
  3. Applies configurable trust thresholds
Trust Score >= Threshold → Accept
Trust Score < Threshold → Reject
In Whitelist → Always Accept