Nostr Mail Bridge
A bridge is what lets a Nostr Mail user and an ordinary email user write to each other. It speaks SMTP on one side, Nostr on the other, and converts nothing but the transport: the RFC 2822 message crosses unchanged.
Reference implementation: nogringo/nostr-mail-bridge.
Architecture
INBOUND (Email → Nostr)
┌─────────────┐ ┌─────────────────┐ ┌────────┐ ┌──────┐
│ Legacy Email│ ───► │ bridge-inbound │ ───► │ Relays │ ───► │ User │
└─────────────┘ │ smtp or mailgun │ └────────┘ └──────┘
└─────────────────┘
OUTBOUND (Nostr → Email)
┌──────┐ ┌────────┐ ┌─────────────────┐ ┌─────────────┐
│ User │ ───► │ Relays │ ───► │ bridge-outbound │ ───► │ Legacy Email│
└──────┘ └────────┘ └─────────────────┘ └─────────────┘
DISCOVERY
┌────────┐ GET /.well-known/nostr.json ┌───────────────┐
│ Client │ ──────────────────────────────────► │ nip05-service │
└────────┘ └───────────────┘
Components
Quick Navigation
Email to Nostr: SMTP server or Mailgun webhooks
Nostr to email: subscribe, unwrap, send
Kind 7679 notifications, and why they name nobody
Accept, reject or silently drop
Environment variables and DNS
What a bridge has to honour
The routing tags
An outbound kind 1301 rumor carries its envelope in its tags, not in the
headers. Read them from the tags, not from To::
Inbound, the bridge sets mail-from on the rumor it builds, so the recipient's
client can tell a bridged email from a native one. There is no rcpt-to
inbound: the recipient is the p tag of the gift wrap.
The user's public settings
A user's kind 30078 event at d = nostr-mail/settings is public because
bridges read it:
Delivery status
Report every send with a kind 7679 event. See Delivery Status.
Quick Start with Docker
git clone https://github.com/nogringo/nostr-mail-bridge
cd nostr-mail-bridge
cp .env.example .env
# Fill in the keys and your domain
docker compose up -d
The compose file runs the inbound SMTP service, the outbound bridge, the NIP-05 service and a Postfix with DKIM signing.
Another way in
Receiving mail does not have to mean running an SMTP server. A Haraka or Mailgun front end can hand raw MIME to a webhook that does the Nostr side:
-
haraka-webhook: a Haraka receiver that spools inbound mail and forwards it to a Mailgun-style MIME webhook.
-
nostr-mail-inbound-webhook: a Dart webhook that resolves recipients, gift wraps the MIME and publishes it, Blossom included for large messages.
-
nmail-api: NIP-05 identity and inbound mail policy, which is also where the alias protocol is served.
Self-Hosted Setup
┌──────────────────────────────────────────────────┐
│ Your Server │
│ │
│ ┌──────────────┐ ┌────────┐ ┌──────────────┐ │
│ │bridge-inbound│ │outbound│ │ nip05-service│ │
│ │ :25 │ │ │ │ :3000 │ │
│ └──────────────┘ └────────┘ └──────────────┘ │
│ │ │ │ │
│ └──────────────┼──────────────┘ │
│ │ │
│ ┌────────┐ │
│ │ Postfix│ (or any MTA) │
│ └────────┘ │
└──────────────────────────────────────────────────┘
Required DNS:
- MX record pointing to your server
- A or AAAA record for the mail domain
- SPF, DKIM and DMARC, or your mail will be filed as spam
Publish the bridge pubkey under _smtp in your /.well-known/nostr.json, which
is how a client discovers it.