cloudflare-email 0.4.0 → 0.4.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +59 -2
- data/README.md +63 -379
- data/SECURITY.md +1 -9
- data/docs/activerecord-tenanted.md +0 -11
- data/docs/custom-ingress.md +1 -15
- data/docs/delivery-events.md +2 -5
- data/docs/features.md +49 -123
- data/docs/getting-started.md +90 -172
- data/docs/mailboxes.md +24 -60
- data/docs/management-engine.md +1 -3
- data/docs/outbox.md +5 -6
- data/docs/reference.md +190 -0
- data/docs/routing-diagnostics.md +0 -2
- data/docs/thread-correlation.md +2 -4
- data/docs/troubleshooting.md +4 -4
- data/lib/cloudflare/email/client.rb +23 -5
- data/lib/cloudflare/email/ingress.rb +16 -4
- data/lib/cloudflare/email/version.rb +1 -1
- data/templates/deploy-to-cloudflare/README.md +8 -10
- data/templates/deploy-to-cloudflare/docs/domain-setup.md +86 -166
- data/templates/worker/README.md +6 -8
- data/templates/worker/docs/domain-setup.md +86 -166
- data/templates/worker/docs/durable-inbound.md +4 -16
- metadata +4 -19
- data/docs/architecture.md +0 -105
- data/docs/tenant-mailboxes-plan.md +0 -149
- data/docs/upgrading-0.2.md +0 -101
- data/docs/upgrading-0.3.md +0 -55
- data/docs/verification/2026-09-10-followup.md +0 -49
- data/docs/verification/2026-09-10-install-upgrade.md +0 -86
- data/docs/verification/2026-09-10-live.md +0 -102
- data/docs/verification/2026-09-10.md +0 -175
- data/docs/verification/2026-09-11-custom-ingress.md +0 -49
- data/docs/verification/2026-09-11-extraction.md +0 -32
- data/docs/verification/2026-09-11-outbound-ledger.md +0 -60
- data/docs/verification/2026-09-11-security.md +0 -76
- data/docs/verification/2026-09-11-tenant-mailboxes.md +0 -75
- data/docs/verification/2026-09-12-destination-context.md +0 -46
- data/docs/verification/2026-09-13-action-mailbox-core.md +0 -96
- data/docs/verification/2026-09-13-durable-inbound-and-routing.md +0 -71
data/docs/custom-ingress.md
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
# Use your existing inbound email pipeline
|
|
2
2
|
|
|
3
|
-
This integration API is available in gem version 0.3.0.
|
|
4
|
-
|
|
5
3
|
You can adopt mailbox registration without replacing your Worker, document processing, sender review, or archive. Use the gem to authenticate the request and resolve an accepted address, then keep your application's policy and processing.
|
|
6
4
|
|
|
7
5
|
A useful starting point is one mailbox per site: an organization owns a receiving domain, a site owns a mailbox, and that mailbox can have several accepted addresses. This is an application convention, not a requirement of the gem. Database tenancy is optional; a single SQLite database works too.
|
|
@@ -181,7 +179,7 @@ const headers = await signedEmailHeaders({
|
|
|
181
179
|
// Send exactly raw with these headers using your existing transport.
|
|
182
180
|
```
|
|
183
181
|
|
|
184
|
-
Here `cloudflare-ingress.js` is your local copy of the template's `src/index.js`. `raw` and `archiveKey` come from your existing bounded reader and archive operation. See the [Worker integration instructions](../templates/worker/README.md) for supported metadata values and forwarding behavior.
|
|
182
|
+
Here `cloudflare-ingress.js` is your local copy of the template's `src/index.js`. `raw` and `archiveKey` come from your existing bounded reader and archive operation. See the [Worker integration instructions](../templates/worker/README.md) for supported metadata values and forwarding behavior.
|
|
185
183
|
|
|
186
184
|
In your Action Mailbox handler, read persisted metadata through the gem:
|
|
187
185
|
|
|
@@ -197,15 +195,3 @@ This reads verified Active Storage metadata, not similarly named MIME headers. A
|
|
|
197
195
|
**A valid Worker signature proves that your trusted Worker submitted the request. It does not prove the original email sender is authentic.** Even metadata labelled `source: "cloudflare"` is an assertion made by the holder of the ingress secret. Do not copy sender-controlled MIME headers into a “trusted authentication” field. If your existing Worker has reliable authentication evidence, preserve its documented provenance and let your application decide how that evidence affects acceptance. The gem intentionally does not infer SPF/DMARC results or implement a sender allowlist.
|
|
198
196
|
|
|
199
197
|
Retries with the same recipient, raw bytes, and metadata context reuse the inbound record. Aliases receive distinct membership context. Changing signed metadata produces a distinct checksum: stable values are preferable to per-attempt timestamps or random IDs. Keep downstream document extraction and external side effects idempotent too.
|
|
200
|
-
|
|
201
|
-
## Adopt incrementally
|
|
202
|
-
|
|
203
|
-
1. Inventory existing accepted addresses, organization/subdomain mappings, fallback destinations, and disabled sites. Backfill records explicitly; do not silently create a mailbox for every incoming address.
|
|
204
|
-
2. Confirm existing Worker routing for one organization's exact domain and activate only verified addresses. Exercise an alias, unknown address, suspended mailbox, and cross-tenant destination with synthetic mail.
|
|
205
|
-
3. Add request verification and preserve your current sender review, archive behavior, error responses, and size limits. Test that rejected or held mail cannot reach the processing queue.
|
|
206
|
-
4. Connect site provisioning to the same mailbox API used by the [management engine](management-engine.md). Scope the engine through existing host authentication and authorization.
|
|
207
|
-
5. Retire duplicate infrastructure only after comparing delivery and recovery behavior. Inbound adoption does not require switching your outbound provider.
|
|
208
|
-
|
|
209
|
-
Keep the shared operational communications history separate from tenant-local raw emails and mailbox membership when those serve different purposes. Define who owns deletion, archive retention, deduplication, and processing status; two competing delivery ledgers make recovery harder.
|
|
210
|
-
|
|
211
|
-
The gem's custom-ingress integration test exercises a custom controller against two real SQLite tenant databases, preserving binary MIME bytes and signed metadata before routing jobs run. It also verifies rejected metadata, host review, accepted-address enforcement, and retry behavior. It does not verify a deployed customer's Cloudflare DNS, Worker authentication evidence, R2 archive, or document-processing pipeline; those require that host's end-to-end tests.
|
data/docs/delivery-events.md
CHANGED
|
@@ -118,11 +118,8 @@ existing state, and a nonterminal event cannot replace terminal state. A later
|
|
|
118
118
|
terminal complaint can replace delivery. Invalid timestamps raise. Match records
|
|
119
119
|
and lock recipient state before using this ordering helper.
|
|
120
120
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
receipt tables from the earlier preproduction adapter must normalize stored
|
|
124
|
-
`message_id` values with `MessageId.normalize` before using filtered replay;
|
|
125
|
-
raw payloads remain unchanged. The reference inbox's outbox migration does this.
|
|
121
|
+
Schedule polling and replay in your application, and choose a retention period
|
|
122
|
+
for delivery receipts.
|
|
126
123
|
|
|
127
124
|
Poll a single batch:
|
|
128
125
|
|
data/docs/features.md
CHANGED
|
@@ -1,138 +1,64 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Features
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
Cloudflare Email handles sending, receiving, and delivery tracking. Mailbox Kit
|
|
4
|
+
adds persistent inboxes and a management UI. Start with the
|
|
5
|
+
[Rails quickstart](getting-started.md).
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
features are available in **0.4.0**, which includes **Mailbox Kit 0.1.0**. Database multi-tenancy is **off by default**:
|
|
9
|
-
the optional mailbox module works in one database unless you explicitly configure
|
|
10
|
-
a tenant connection adapter. A mailbox tenant key alone does not switch databases.
|
|
7
|
+
## Sending
|
|
11
8
|
|
|
12
|
-
|
|
9
|
+
- Use existing Action Mailer templates or the plain Ruby client.
|
|
10
|
+
- Send text, HTML, attachments, multipart messages, cc/bcc, and replies.
|
|
11
|
+
- Save outgoing messages in a [durable outbox](outbox.md) with a history of attempts and recipient outcomes.
|
|
12
|
+
- Retry jobs using the saved send operation; uncertain sends stay available for review.
|
|
13
|
+
- Track delivered, deferred, bounced, failed, rejected, and complained messages through [delivery events](delivery-events.md).
|
|
14
|
+
- [Match replies](thread-correlation.md) using Cloudflare's returned message ID.
|
|
13
15
|
|
|
14
|
-
|
|
15
|
-
| --- | --- | --- |
|
|
16
|
-
| Create managed inboxes in code | Optional `Mailboxes` module | Named mailboxes, aliases, ownership references, read/archive state and retained raw mail |
|
|
17
|
-
| Isolate organizations in separate databases | Optional `Tenancy` adapter | Configurable ActiveRecord base, tenant-aware jobs and shared-to-tenant event replay; tested with SQLite and `activerecord-tenanted` |
|
|
18
|
-
| Send from an ordinary Ruby program | `Cloudflare::Email::Client` | Structured messages or complete raw MIME; no Rails/database required |
|
|
19
|
-
| Send existing Rails mailers | ActionMailer delivery method | Your mailer templates, attachments, multipart bodies, cc/bcc and reply headers sent through Cloudflare |
|
|
20
|
-
| Receive email in Rails | Email Worker + ActionMailbox | Unchanged raw MIME, attachments, authenticated SMTP envelope metadata and duplicate handling |
|
|
21
|
-
| Develop against real incoming email locally | `cloudflare:email:dev` | A temporary tunnel restricted to your email ingress |
|
|
22
|
-
| Save an email before sending it | Optional ActiveRecord outbox | Immutable message snapshot, saved recipients, a send claim, and per-recipient results |
|
|
23
|
-
| Avoid accidentally resending after a crash or timeout | `SendJob` + outbox | Jobs use the saved operation identity; uncertain attempts stay blocked for review |
|
|
24
|
-
| Track delivery, bounce or complaint updates | `EventConsumer` + Cloudflare Queue | Validated lifecycle events, account/domain checks and acknowledgement after successful handling |
|
|
25
|
-
| Avoid building your own event ledger | Optional tracking tables | Durable receipts, duplicate/conflict detection, unmatched-event storage and replay |
|
|
26
|
-
| Connect events to outbox recipients | `DeliveryEvents` | Account/message/recipient matching, event ordering and callbacks for your app's records |
|
|
27
|
-
| Investigate an uncertain send | Recovery tasks + `Outbox.reconcile` | Operator-supplied evidence and an audit trail; no automatic resend based on elapsed time |
|
|
28
|
-
| Match replies to earlier messages | Provider message IDs + `MessageId.normalize` | A consistent lookup key for your app's conversations |
|
|
29
|
-
| Observe email processing | ActiveSupport notifications + `doctor` | Send/ingress/event/outbox instrumentation and configuration diagnostics |
|
|
30
|
-
| Deploy receiving infrastructure | Ruby deployer and routing tasks | Environment-specific Workers, ingress secrets, address routes and DNS preflight checks |
|
|
16
|
+
## Receiving
|
|
31
17
|
|
|
32
|
-
|
|
18
|
+
- Deploy the included [Cloudflare Worker](../templates/deploy-to-cloudflare/README.md).
|
|
19
|
+
- Retain incoming mail in R2 and retry while Rails is unavailable.
|
|
20
|
+
- Verify signed requests and the actual SMTP recipient before storing email.
|
|
21
|
+
- Keep original messages and attachments in Rails Action Mailbox.
|
|
22
|
+
- Deduplicate repeated deliveries.
|
|
23
|
+
- Process email with ordinary Rails mailbox handlers.
|
|
24
|
+
- Test incoming email locally with `bin/rails cloudflare:email:dev`.
|
|
33
25
|
|
|
34
|
-
|
|
35
|
-
saves raw email and its envelope in R2 before the Worker returns successfully.
|
|
36
|
-
Queues attempt the Rails handoff, and a scheduled pass recovers retained mail
|
|
37
|
-
after enqueue failures or retry exhaustion. Provision the storage, queue and
|
|
38
|
-
schedule before deploying; direct forwarding requires an explicit fallback setting.
|
|
26
|
+
## Inboxes
|
|
39
27
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
28
|
+
- Create named [mailboxes](mailboxes.md), aliases, and optional catch-all addresses.
|
|
29
|
+
- Associate mailboxes with application records through `owner_ref`.
|
|
30
|
+
- List unread messages, mark them read, archive them, and retain their originals.
|
|
31
|
+
- Suspend mailboxes while reserving their addresses.
|
|
32
|
+
- Send from an active mailbox through the outbox.
|
|
33
|
+
- Mount a [server-rendered management UI](management-engine.md) using your app's login and permissions.
|
|
34
|
+
- Use [customer subdomains](../templates/worker/docs/domain-setup.md) with Rails address lookup.
|
|
44
35
|
|
|
45
|
-
|
|
46
|
-
verify bounded raw messages before tenant lookup, keep their own storage, or
|
|
47
|
-
persist through ActionMailbox. Optional v3 signatures authenticate custom Worker
|
|
48
|
-
metadata; your application still decides whether its provenance and sender policy
|
|
49
|
-
are acceptable. The bundled Worker continues to use v2 by default.
|
|
36
|
+
## Storage and providers
|
|
50
37
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
`Mailboxes.with_recipient` offers the same destination context for applications
|
|
54
|
-
that own their storage. Neither replaces your sender policy or business workflow.
|
|
38
|
+
SQLite and PostgreSQL are supported. Database multi-tenancy is **off by default**;
|
|
39
|
+
you can configure [separate SQLite databases](activerecord-tenanted.md) when needed.
|
|
55
40
|
|
|
56
|
-
[
|
|
57
|
-
and
|
|
58
|
-
incomplete inspection; a passing snapshot does not prove live email delivery.
|
|
41
|
+
[Mailbox Kit](../mailbox-kit/README.md) works with other Action Mailbox integrations.
|
|
42
|
+
Incoming and outgoing mail can use different providers.
|
|
59
43
|
|
|
60
|
-
##
|
|
44
|
+
## Administration
|
|
61
45
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
46
|
+
Use [configuration checks and tasks](reference.md#observability-and-permissions),
|
|
47
|
+
[routing diagnostics](routing-diagnostics.md), and ActiveSupport notifications to
|
|
48
|
+
monitor your integration. Custom endpoints can reuse the [ingress verification API](custom-ingress.md).
|
|
49
|
+
[Routing analytics](routing-deliveries.md) can supply additional delivery evidence
|
|
50
|
+
for qualifying messages to verified Routing destinations.
|
|
66
51
|
|
|
67
|
-
|
|
68
|
-
outbox identity; it does not clear an uncertain send claim. Keep application
|
|
69
|
-
writes and gem callbacks on the same database connection when they must commit
|
|
70
|
-
together. See [outbox setup](outbox.md).
|
|
52
|
+
## What the statuses mean
|
|
71
53
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
| Result | What it tells you |
|
|
54
|
+
| Status | Meaning |
|
|
75
55
|
| --- | --- |
|
|
76
|
-
|
|
|
77
|
-
| `
|
|
78
|
-
|
|
|
79
|
-
|
|
|
80
|
-
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
subscriptions. The [getting-started guide](getting-started.md) walks through each.
|
|
86
|
-
|
|
87
|
-
## Protections included
|
|
88
|
-
|
|
89
|
-
By default, incoming requests use v2 HMAC signatures covering the timestamp, SMTP sender,
|
|
90
|
-
SMTP recipient and raw message. Rails checks a five-minute timestamp window.
|
|
91
|
-
Identical MIME retried for the same recipient is deduplicated; delivery to a
|
|
92
|
-
different recipient is stored separately.
|
|
93
|
-
|
|
94
|
-
Rails and the Worker default to a **25 MiB raw incoming message limit**. Both
|
|
95
|
-
support a positive `MAX_EMAIL_BYTES` override. Remote endpoints require HTTPS,
|
|
96
|
-
the Worker refuses redirects and limits its Rails request to 15 seconds, and
|
|
97
|
-
the development tunnel only routes ingress POSTs. Client inspection and default
|
|
98
|
-
retry/mailbox logging omit sensitive details.
|
|
99
|
-
|
|
100
|
-
The outbox preserves the rendered message and acceptance evidence, blocks
|
|
101
|
-
ambiguous resend attempts, and records reconciliation decisions. Delivery-event
|
|
102
|
-
validation rejects malformed schema fields; original receipt identity and
|
|
103
|
-
payload are read-only through normal ActiveRecord updates. These safeguards do
|
|
104
|
-
not make database administrators untrusted or provide exactly-once delivery.
|
|
105
|
-
|
|
106
|
-
See [managed mailbox setup](mailboxes.md) for the application-facing API and
|
|
107
|
-
[activerecord-tenanted](activerecord-tenanted.md) for separate SQLite databases.
|
|
108
|
-
|
|
109
|
-
## What belongs in your mailbox application
|
|
110
|
-
|
|
111
|
-
The gem supplies email infrastructure and optional managed mailbox records.
|
|
112
|
-
Your app supplies users, mailbox permissions, conversation records, custom folders, search, compose screens,
|
|
113
|
-
drafts and any AI review or approval workflow. It also decides unsubscribe and
|
|
114
|
-
recipient eligibility policy. The optional [management engine](management-engine.md)
|
|
115
|
-
adds server-rendered mailbox administration and plain-text message previews.
|
|
116
|
-
It does not supply a compose/conversation product or AI agent.
|
|
117
|
-
|
|
118
|
-
An authenticated SMTP envelope tells your app which address Cloudflare received
|
|
119
|
-
the message for. It does not prove the human sender's identity. Reply correlation
|
|
120
|
-
also does not authorize access to a conversation. Apply your app's permissions
|
|
121
|
-
before displaying mail or sending a reply.
|
|
122
|
-
|
|
123
|
-
You configure Cloudflare domains/DNS, queue subscriptions, durable jobs,
|
|
124
|
-
monitoring, storage protection and retention. Direct forwarding does not buffer
|
|
125
|
-
Rails outages; the optional durable inbound setup retains pending mail in R2.
|
|
126
|
-
See [architecture](architecture.md)
|
|
127
|
-
for the full boundary and [security guidance](../SECURITY.md) for deployment.
|
|
128
|
-
|
|
129
|
-
## Where to go next
|
|
130
|
-
|
|
131
|
-
- [Build your first integration](getting-started.md)
|
|
132
|
-
- [Use plain Ruby, attachments or raw MIME](../README.md#plain-ruby)
|
|
133
|
-
- [Configure client retries and errors](../README.md#retry-and-configuration)
|
|
134
|
-
- [Set up event polling and durable receipts](delivery-events.md)
|
|
135
|
-
- [Recover saved outbound operations](outbox.md)
|
|
136
|
-
- [Correlate replies](thread-correlation.md)
|
|
137
|
-
- [Use Cloudflare SMTP instead of the HTTP delivery method](../README.md#smtp-alternative)
|
|
138
|
-
- [Troubleshoot your setup](troubleshooting.md)
|
|
56
|
+
| Send accepted | Cloudflare accepted the send; delivery updates may follow |
|
|
57
|
+
| Outbox `partial` | Recipients have different acceptance outcomes; inspect each recipient |
|
|
58
|
+
| Delivery event `delivered` | The receiving mail server accepted the message |
|
|
59
|
+
| Inbound Rails `delivered` | Your mailbox handler finished processing |
|
|
60
|
+
| Inbox read / archived | Your application's inbox state |
|
|
61
|
+
|
|
62
|
+
Your app supplies users, permissions, sender acceptance rules, and business
|
|
63
|
+
processing. The management UI covers administration and message reading; compose
|
|
64
|
+
screens, conversations, search, and AI workflows belong in your app.
|