docs: document Cloudflare Email Workers as primary ingestion method

Add setup instructions for Cloudflare Email Routing (Option A, recommended)
and reposition ForwardEmail as an optional alternative (Option B). Clarify
Cloudflare free plan includes Workers, KV, and Email Routing. Fix security
note scope and remove redundant/misleading content.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Julien Herr
2026-05-21 00:36:44 +02:00
parent 1c8b0ca194
commit ad5bd9a79a
+55 -22
View File
@@ -1,6 +1,6 @@
# Email-to-RSS # Email-to-RSS
Convert email newsletters into a private RSS feed using Cloudflare Workers + ForwardEmail. Convert email newsletters into a private RSS feed using Cloudflare Workers.
This project is self-hosted, uses your own domain, and keeps your data in your own Cloudflare account. This project is self-hosted, uses your own domain, and keeps your data in your own Cloudflare account.
@@ -17,33 +17,46 @@ Email-to-RSS keeps the same workflow while avoiding shared domains and shared da
- Inline double-confirm delete interactions with toast feedback in the admin dashboard - Inline double-confirm delete interactions with toast feedback in the admin dashboard
- Resizable + sortable table columns in the admin dashboard (Table view) - Resizable + sortable table columns in the admin dashboard (Table view)
- Unique newsletter addresses per feed (for example `apple.mountain.42@yourdomain.com`) - Unique newsletter addresses per feed (for example `apple.mountain.42@yourdomain.com`)
- ForwardEmail webhook ingestion with source-IP verification - Cloudflare Email Workers ingestion (no third-party service)
- ForwardEmail webhook ingestion with source-IP verification (optional alternative)
- Optional per-feed sender allowlist (`email@domain.com` or `domain.com`) - Optional per-feed sender allowlist (`email@domain.com` or `domain.com`)
- RSS generation on demand (`/rss/:feedId`) - RSS generation on demand (`/rss/:feedId`)
- Cloudflare KV storage for feed config + email metadata/content - Cloudflare KV storage for feed config + email metadata/content
- Password-protected admin UI - Password-protected admin UI
- Fully self-hosted on your Cloudflare account
## Architecture ## Architecture
1. ForwardEmail forwards inbound messages to `https://yourdomain.com/api/inbound`. Two ingestion methods are supported — pick one or use both:
2. The Worker validates the request source against ForwardEmail MX IP ranges.
3. The Worker parses and stores incoming content in KV. | Method | How it works |
4. `https://yourdomain.com/rss/:feedId` renders RSS from stored items. | ---------------------- | ------------------------------------------------------------------ |
5. `/admin` provides feed management and email deletion. | **Cloudflare Email Workers** | Cloudflare Email Routing delivers the raw message directly to the Worker via the `email()` handler — no outbound webhook needed |
| **ForwardEmail webhook** | ForwardEmail parses the message and POSTs a JSON payload to `POST /api/inbound`; the Worker verifies the source IP before processing |
Common path:
1. Incoming email arrives at `user@yourdomain.com`.
2. The Worker resolves the feed from the recipient address and stores the email in KV.
3. `https://yourdomain.com/rss/:feedId` renders RSS from stored items.
4. `/admin` provides feed management and email deletion.
Main routes: Main routes:
- `src/routes/inbound.ts`: webhook ingestion - `src/lib/cloudflare-email.ts`: Cloudflare Email Workers ingestion
- `src/routes/inbound.ts`: ForwardEmail webhook ingestion
- `src/routes/rss.ts`: RSS rendering - `src/routes/rss.ts`: RSS rendering
- `src/routes/admin.ts`: admin UI + feed CRUD - `src/routes/admin.ts`: admin UI + feed CRUD
## Requirements ## Requirements
- Node.js 20+ - Node.js 20+
- A Cloudflare account - A Cloudflare account (free plan works — Workers, KV, and Email Routing are all included)
- A domain managed in Cloudflare DNS - A domain added to Cloudflare as a zone (DNS managed by Cloudflare)
- A ForwardEmail account - A ForwardEmail account _(Option B only)_
## Cloudflare setup
If your domain is not yet on Cloudflare: in the [Cloudflare dashboard](https://dash.cloudflare.com/), go to *Add a site*, enter your domain, choose the Free plan, and follow the instructions to update your nameservers at your registrar. Wait for the zone to become active (usually a few minutes).
## Setup ## Setup
@@ -56,17 +69,31 @@ Main routes:
```bash ```bash
bash setup.sh bash setup.sh
``` ```
The script will prompt for an admin password and your domain, then:
- install npm dependencies
- verify Cloudflare auth (`wrangler whoami`)
- create KV namespaces (`EMAIL_STORAGE` + preview) in your account
- set the `ADMIN_PASSWORD` secret in the `production` environment
- generate `wrangler.toml` from `wrangler-example.toml` with your KV IDs, domain, and today's compatibility date
`setup.sh` will: 4. Configure email ingestion — choose **one** of the two options below.
- install npm dependencies ### Option A — Cloudflare Email Workers (recommended)
- verify Cloudflare auth (`wrangler whoami`)
- create KV namespaces (`EMAIL_STORAGE` + preview)
- set the `ADMIN_PASSWORD` secret in `production`
- generate `wrangler.toml` from `wrangler-example.toml`
- stamp `compatibility_date` to the current date
4. Configure ForwardEmail DNS records in Cloudflare: No third-party service required. Cloudflare receives the email and hands it directly to the Worker.
1. In the Cloudflare dashboard, go to *Email → Email Routing* for your zone and click **Enable Email Routing**. Cloudflare will prompt you to add MX and SPF records — accept and it adds them automatically.
2. Under *Email Routing → Routing Rules*, add a **Catch-all** rule:
- Action: **Send to Worker**
- Worker: `email-to-rss` (the name from `wrangler.toml`)
That's it. No webhook configuration is needed.
### Option B — ForwardEmail (alternative)
Use this if you prefer ForwardEmail's additional features (sender filtering, open-tracking, etc.).
Add these DNS records in Cloudflare (*DNS → Records*):
| Type | Name | Content | Notes | | Type | Name | Content | Notes |
| ---- | ---- | ---------------------------------------------------- | ----------------------- | | ---- | ---- | ---------------------------------------------------- | ----------------------- |
@@ -75,14 +102,20 @@ Main routes:
| TXT | @ | `"forward-email=https://yourdomain.com/api/inbound"` | webhook target | | TXT | @ | `"forward-email=https://yourdomain.com/api/inbound"` | webhook target |
| TXT | @ | `"v=spf1 include:spf.forwardemail.net -all"` | SPF | | TXT | @ | `"v=spf1 include:spf.forwardemail.net -all"` | SPF |
5. Deploy: Replace `yourdomain.com` with your actual domain.
The Worker verifies each webhook request against ForwardEmail's published MX IP list before processing it.
5. Deploy:
```bash ```bash
npm run deploy npm run deploy
``` ```
Wrangler will create the Worker and register `yourdomain.com` (and `www.yourdomain.com`) as custom domains pointing to it. Cloudflare handles TLS automatically.
6. Open `https://yourdomain.com/admin` and sign in. 6. Open `https://yourdomain.com/admin` and sign in.
> **Tip:** To verify the Worker is running, check *Workers & Pages → email-to-rss* in the Cloudflare dashboard. The *Custom Domains* tab should list your domain once the deploy succeeds.
## Development ## Development
```bash ```bash
@@ -100,7 +133,7 @@ npm run build
## Security notes ## Security notes
- Inbound webhook access is IP-restricted to ForwardEmail MX sources. - When using Option B (ForwardEmail), inbound webhook access is IP-restricted to ForwardEmail MX sources.
- Admin auth uses a signed, `HttpOnly`, `Secure`, `SameSite=Strict` cookie. - Admin auth uses a signed, `HttpOnly`, `Secure`, `SameSite=Strict` cookie.
- Admin responses are `no-store` to avoid cache leakage. - Admin responses are `no-store` to avoid cache leakage.
- For high-value feeds, set `Allowed senders` so only known sender addresses/domains are accepted. - For high-value feeds, set `Allowed senders` so only known sender addresses/domains are accepted.