cloudflare-email 0.4.0 → 0.4.1
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 +31 -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/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 +2 -17
- 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/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.
|
data/docs/getting-started.md
CHANGED
|
@@ -1,26 +1,15 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Set up email in Rails
|
|
2
2
|
|
|
3
|
-
This guide
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
This guide covers sending, receiving, and delivery tracking. For persistent
|
|
4
|
+
inboxes and a management UI, continue with [mailboxes](mailboxes.md).
|
|
5
|
+
SQLite works throughout.
|
|
6
6
|
|
|
7
|
-
You
|
|
8
|
-
|
|
9
|
-
tested floors are 7.2.3.2, 8.0.5.1 and 8.1.3.1. Rails 8 requires Ruby 3.3+.
|
|
10
|
-
Ruby 4 is tested with Rails 8.1. Plain Ruby users can use the
|
|
11
|
-
[standalone client](../README.md#plain-ruby) instead.
|
|
7
|
+
You need Rails 7.2–8.1, a Cloudflare account, and a domain you control.
|
|
8
|
+
These examples use `mail.example.com` for sending and `in.example.com` for receiving.
|
|
12
9
|
|
|
13
|
-
|
|
14
|
-
`in.example.com` for receiving. Replace them with subdomains you own. They do
|
|
15
|
-
not need to be the same domain as your Rails application's web address.
|
|
10
|
+
## 1. Install
|
|
16
11
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
### Install the current code
|
|
20
|
-
|
|
21
|
-
Add version 0.4 to your app's `Gemfile`. Existing mailbox installations should read
|
|
22
|
-
the [core upgrade guide](../mailbox-kit/docs/upgrading.md). When upgrading from 0.2,
|
|
23
|
-
also read the [Worker upgrade guide](upgrading-0.3.md):
|
|
12
|
+
Add to your Gemfile:
|
|
24
13
|
|
|
25
14
|
```ruby
|
|
26
15
|
gem "cloudflare-email", "~> 0.4.0"
|
|
@@ -28,44 +17,42 @@ gem "cloudflare-email", "~> 0.4.0"
|
|
|
28
17
|
|
|
29
18
|
```sh
|
|
30
19
|
bundle install
|
|
31
|
-
bin/rails generate cloudflare:email:install
|
|
20
|
+
bin/rails generate cloudflare:email:install
|
|
21
|
+
bin/rails db:migrate
|
|
32
22
|
```
|
|
33
23
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
24
|
+
Follow the prompts to install Action Mailbox and the default mailbox.
|
|
25
|
+
The generator also configures Action Mailer and copies the Worker template.
|
|
26
|
+
For a send-only app, use `--no-inbound` and skip the receiving steps below.
|
|
37
27
|
|
|
38
|
-
|
|
28
|
+
## 2. Send your first email
|
|
39
29
|
|
|
40
|
-
In Cloudflare, open **Compute → Email Service → Email Sending
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
configuration separate. Do not replace existing apex mail records casually.
|
|
30
|
+
In Cloudflare, open **Compute → Email Service → Email Sending**, add
|
|
31
|
+
`mail.example.com`, and complete its DNS verification. A sending subdomain lets
|
|
32
|
+
you keep your existing domain's mail provider.
|
|
44
33
|
|
|
45
|
-
Create
|
|
46
|
-
to the environment where Rails runs, or use the equivalent Rails credentials:
|
|
34
|
+
Create a token with Email Sending permission and set:
|
|
47
35
|
|
|
48
36
|
```sh
|
|
49
37
|
export CLOUDFLARE_ACCOUNT_ID=your-account-id
|
|
50
38
|
export CLOUDFLARE_API_TOKEN=your-email-sending-token
|
|
51
39
|
```
|
|
52
40
|
|
|
53
|
-
Rails credentials
|
|
54
|
-
Nonempty Rails credentials take precedence
|
|
55
|
-
|
|
41
|
+
Alternatively, use Rails credentials `cloudflare.account_id` and
|
|
42
|
+
`cloudflare.api_token`. Nonempty Rails credentials take precedence.
|
|
43
|
+
Restart Rails after changing configuration.
|
|
56
44
|
|
|
57
|
-
|
|
45
|
+
Check the configuration and send to an inbox you control:
|
|
58
46
|
|
|
59
47
|
```sh
|
|
60
48
|
bin/rails cloudflare:email:doctor
|
|
61
49
|
FROM=hello@mail.example.com TO=you@example.net bin/rails cloudflare:email:send_test
|
|
62
50
|
```
|
|
63
51
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
including checking spam. A diagnostics success alone does not prove delivery.
|
|
52
|
+
Check the destination inbox and spam folder. `doctor` checks configuration;
|
|
53
|
+
`send_test` sends the message.
|
|
67
54
|
|
|
68
|
-
### Use your
|
|
55
|
+
### Use your mailers
|
|
69
56
|
|
|
70
57
|
```ruby
|
|
71
58
|
# app/mailers/hello_mailer.rb
|
|
@@ -76,80 +63,48 @@ class HelloMailer < ApplicationMailer
|
|
|
76
63
|
end
|
|
77
64
|
end
|
|
78
65
|
end
|
|
79
|
-
```
|
|
80
|
-
|
|
81
|
-
From `bin/rails console`:
|
|
82
66
|
|
|
83
|
-
```ruby
|
|
84
67
|
HelloMailer.hello("you@example.net").deliver_now
|
|
85
68
|
```
|
|
86
69
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
background delivery. For saved send history and protection against ambiguous
|
|
90
|
-
resends, use the outbox in part 3. Do not send the same message through both paths.
|
|
70
|
+
Use `deliver_later` with your app's job backend for background sending.
|
|
71
|
+
HTML templates, attachments, multipart messages, and cc/bcc work through Action Mailer.
|
|
91
72
|
|
|
92
|
-
##
|
|
93
|
-
|
|
94
|
-
Incoming email follows this path:
|
|
73
|
+
## 3. Receive email
|
|
95
74
|
|
|
96
75
|
```text
|
|
97
|
-
Sender → Cloudflare Email Routing →
|
|
76
|
+
Sender → Cloudflare Email Routing → Worker → Rails Action Mailbox → your handler
|
|
98
77
|
```
|
|
99
78
|
|
|
100
|
-
The Worker
|
|
101
|
-
|
|
79
|
+
The Worker saves incoming email at Cloudflare and retries delivery while Rails
|
|
80
|
+
is unavailable.
|
|
102
81
|
|
|
103
|
-
###
|
|
82
|
+
### Deploy the Worker
|
|
104
83
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
```
|
|
84
|
+
Save the generated secret as `cloudflare.ingress_secret` in Rails credentials
|
|
85
|
+
or `CLOUDFLARE_INGRESS_SECRET`. Deploy Rails with Active Storage and a running
|
|
86
|
+
job worker, then follow the [Deploy to Cloudflare guide](../templates/deploy-to-cloudflare/README.md).
|
|
109
87
|
|
|
110
|
-
|
|
111
|
-
If you already have custom mailbox routing or initializer changes, inspect the
|
|
112
|
-
generator's overwrite prompts before accepting. The generated default mailbox
|
|
113
|
-
only logs receipt; you will add your product's processing logic.
|
|
88
|
+
The deployment form asks for:
|
|
114
89
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
the Worker. Use a separate deployment token as `cloudflare.management_token` or
|
|
118
|
-
`CLOUDFLARE_MANAGEMENT_TOKEN`; [the permission table](../README.md#observability-and-permissions)
|
|
119
|
-
lists the scopes needed for Worker deployment and routing.
|
|
90
|
+
- **RAILS_INGRESS_URL:** `https://app.example.com/rails/action_mailbox/cloudflare/inbound_emails`
|
|
91
|
+
- **INGRESS_SECRET:** the same secret configured in Rails
|
|
120
92
|
|
|
121
|
-
### Connect
|
|
93
|
+
### Connect an address
|
|
122
94
|
|
|
123
|
-
In Cloudflare
|
|
124
|
-
|
|
125
|
-
|
|
95
|
+
In Cloudflare Email Routing, configure `in.example.com` and its receiving DNS
|
|
96
|
+
records. Create a rule for `support@in.example.com` with **Send to a Worker**,
|
|
97
|
+
selecting the deployed Worker.
|
|
126
98
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
It provisions R2 and Queues and prompts for your Rails URL and shared secret.
|
|
130
|
-
Then select that Worker in your Email Routing rule as described in its guide.
|
|
99
|
+
Sending-domain verification and receiving routes are separate.
|
|
100
|
+
For many addresses or customer subdomains, use the [domain setup guide](../templates/worker/docs/domain-setup.md).
|
|
131
101
|
|
|
132
|
-
|
|
133
|
-
|
|
102
|
+
Send a test email to `support@in.example.com`. Confirm Rails stores it and
|
|
103
|
+
the mailbox job finishes.
|
|
134
104
|
|
|
135
|
-
|
|
136
|
-
configuration using the [durable setup guide](../templates/worker/docs/durable-inbound.md).
|
|
137
|
-
The Ruby task below updates that provisioned Worker while preserving its bindings.
|
|
138
|
-
It refuses a missing bucket/queue binding or recovery schedule before changing code.
|
|
105
|
+
### Process the email
|
|
139
106
|
|
|
140
|
-
|
|
141
|
-
RAILS_ENV=production bin/rails cloudflare:email:deploy_worker URL=https://app.example.com/rails/action_mailbox/cloudflare/inbound_emails
|
|
142
|
-
RAILS_ENV=production bin/rails cloudflare:email:provision_route ADDRESS=support@in.example.com
|
|
143
|
-
```
|
|
144
|
-
|
|
145
|
-
The first command deploys the Worker; the second routes the address to it.
|
|
146
|
-
Send a real test email to `support@in.example.com`, then check the ActionMailbox
|
|
147
|
-
record and processing job. A web request accepted by Rails means storage worked;
|
|
148
|
-
the mailbox job can still need attention.
|
|
149
|
-
|
|
150
|
-
### Read the message in your mailbox
|
|
151
|
-
|
|
152
|
-
Inside the generated `MainMailbox#process`, these values are available:
|
|
107
|
+
Add your application logic to the generated `MainMailbox#process`:
|
|
153
108
|
|
|
154
109
|
```ruby
|
|
155
110
|
envelope = Cloudflare::Email::Envelope.for(inbound_email)
|
|
@@ -160,41 +115,32 @@ attachments = mail.attachments
|
|
|
160
115
|
raw_message = inbound_email.raw_email.download
|
|
161
116
|
```
|
|
162
117
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
can be absent for records from another ingress; handle that case explicitly.
|
|
167
|
-
Treat message contents and attachments as untrusted input when displaying them.
|
|
118
|
+
Use the verified `receiving_address` for mailbox or organization lookup.
|
|
119
|
+
The MIME `To` header can differ, especially for Bcc. Treat message contents
|
|
120
|
+
and attachments as untrusted input.
|
|
168
121
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
provider message ID and compare reply headers within your application's mailbox
|
|
172
|
-
scope. The outbox saves this ID for you. See [thread correlation](thread-correlation.md).
|
|
122
|
+
Set `reply_to: "support@in.example.com"` in outgoing mail to receive replies here.
|
|
123
|
+
See [reply matching](thread-correlation.md) for conversations.
|
|
173
124
|
|
|
174
|
-
###
|
|
125
|
+
### Test with a local Rails server
|
|
175
126
|
|
|
176
|
-
Install `cloudflared`,
|
|
177
|
-
|
|
127
|
+
Install `cloudflared`, start Rails, and configure a separate development address.
|
|
128
|
+
Then run:
|
|
178
129
|
|
|
179
130
|
```sh
|
|
180
131
|
INBOUND_DELIVERY_MODE=direct bin/rails cloudflare:email:deploy_worker
|
|
181
132
|
bin/rails cloudflare:email:dev
|
|
182
133
|
```
|
|
183
134
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
Use development test data. The tunnel URL is temporary and does not provide an
|
|
189
|
-
offline mailbox when your local server is stopped.
|
|
190
|
-
|
|
191
|
-
## 3. Add the durable outbox with SQLite
|
|
135
|
+
These commands need a [Worker management token](reference.md#observability-and-permissions).
|
|
136
|
+
Keep the tunnel running while testing. It uses port 3000 by default;
|
|
137
|
+
set `PORT=...` for another port. Direct mode does not retain mail while
|
|
138
|
+
your local server is stopped.
|
|
192
139
|
|
|
193
|
-
|
|
194
|
-
that saved operation instead of re-rendering and resending mail on every retry.
|
|
195
|
-
This is useful for compose, replies, invoices and approved drafts.
|
|
140
|
+
## 4. Save outgoing messages
|
|
196
141
|
|
|
197
|
-
|
|
142
|
+
The outbox saves a message before sending, tracks each recipient, and protects
|
|
143
|
+
against accidental resends after a timeout.
|
|
198
144
|
|
|
199
145
|
```sh
|
|
200
146
|
bin/rails generate cloudflare:email:tracking
|
|
@@ -202,33 +148,26 @@ bin/rails generate cloudflare:email:outbox
|
|
|
202
148
|
bin/rails db:migrate
|
|
203
149
|
```
|
|
204
150
|
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
backend and run a worker that processes the `mailers` queue; the gem does not
|
|
208
|
-
install the backend for you.
|
|
151
|
+
If you are setting up [managed mailboxes](mailboxes.md), use that generator
|
|
152
|
+
instead—it includes both of these.
|
|
209
153
|
|
|
210
|
-
|
|
154
|
+
With a job worker processing the `mailers` queue, try this from Rails console:
|
|
211
155
|
|
|
212
156
|
```ruby
|
|
213
157
|
account_id = Cloudflare::Email::Credentials.account_id
|
|
214
158
|
operation = Cloudflare::Email::ActiveRecord::Outbox.prepare_mail(
|
|
215
159
|
account_id: account_id,
|
|
216
|
-
operation_key: "
|
|
160
|
+
operation_key: "hello:1",
|
|
217
161
|
mail: HelloMailer.hello("you@example.net").message,
|
|
218
162
|
)
|
|
219
163
|
Cloudflare::Email::SendJob.perform_later(account_id, operation.operation_key)
|
|
220
164
|
```
|
|
221
165
|
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
your retry mechanism: generated MIME headers can change, causing a snapshot conflict.
|
|
226
|
-
|
|
227
|
-
In an application transaction, prepare the operation alongside your business
|
|
228
|
-
record, then enqueue only after the outer transaction commits. The detailed
|
|
229
|
-
[outbox guide](outbox.md#prepare-once-dispatch-after-commit) shows that pattern.
|
|
166
|
+
Use one operation key per intended send. For retries, dispatch the saved
|
|
167
|
+
operation rather than rendering and sending the mailer again.
|
|
168
|
+
In application transactions, enqueue after commit.
|
|
230
169
|
|
|
231
|
-
|
|
170
|
+
Inspect the result:
|
|
232
171
|
|
|
233
172
|
```ruby
|
|
234
173
|
operation.reload.state
|
|
@@ -236,26 +175,23 @@ operation.provider_message_id
|
|
|
236
175
|
operation.outbound_recipients.pluck(:recipient, :acceptance_state, :state)
|
|
237
176
|
```
|
|
238
177
|
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
`unknown` or a stuck `sending` operation needs investigation, not a new send key
|
|
242
|
-
as a shortcut. See [states and recovery](outbox.md#states-and-retries).
|
|
178
|
+
See the [outbox guide](outbox.md) for application transactions, callbacks,
|
|
179
|
+
and recovery of uncertain sends.
|
|
243
180
|
|
|
244
|
-
##
|
|
181
|
+
## 5. Track delivery
|
|
245
182
|
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
HTTP pull consumer, retries and a dead-letter queue for repeatedly failing events.
|
|
183
|
+
Create an outbound Cloudflare Queue and Email Sending subscription using the
|
|
184
|
+
[delivery-event setup](delivery-events.md#provision-once). This is a separate
|
|
185
|
+
queue from the inbound Worker queue.
|
|
250
186
|
|
|
251
|
-
|
|
187
|
+
Set:
|
|
252
188
|
|
|
253
189
|
```sh
|
|
254
190
|
export CLOUDFLARE_QUEUES_TOKEN=your-queues-read-write-token
|
|
255
191
|
export CLOUDFLARE_EVENT_QUEUE_ID=your-queue-id
|
|
256
192
|
```
|
|
257
193
|
|
|
258
|
-
With the outbox installed, configure
|
|
194
|
+
With the outbox installed, configure:
|
|
259
195
|
|
|
260
196
|
```ruby
|
|
261
197
|
# config/initializers/cloudflare_delivery_events.rb
|
|
@@ -269,41 +205,23 @@ Rails.application.configure do
|
|
|
269
205
|
end
|
|
270
206
|
```
|
|
271
207
|
|
|
272
|
-
Restart Rails and
|
|
208
|
+
Restart Rails and run:
|
|
273
209
|
|
|
274
210
|
```sh
|
|
275
211
|
bin/rails cloudflare:email:consume_events
|
|
276
|
-
```
|
|
277
|
-
|
|
278
|
-
This command processes **one batch and exits**. Schedule it repeatedly using
|
|
279
|
-
your application's scheduler. Also schedule receipt replay for recovery:
|
|
280
|
-
|
|
281
|
-
```sh
|
|
282
212
|
bin/rails cloudflare:email:replay_events
|
|
283
213
|
```
|
|
284
214
|
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
Your app still decides what those outcomes mean for its UI and future sending.
|
|
290
|
-
|
|
291
|
-
For custom product updates, use the [transactional callbacks](outbox.md#recover-dispatch-and-application-projections).
|
|
292
|
-
If you only want events and have your own delivery models, use the lower-level
|
|
293
|
-
[durable receipt adapter](delivery-events.md#durable-rails-receipts).
|
|
294
|
-
|
|
295
|
-
## Before relying on it
|
|
215
|
+
Each command runs once and exits; schedule both to run regularly.
|
|
216
|
+
Receipts are saved before acknowledgement and matched to the message's
|
|
217
|
+
recipients. Delivery states include delivered, deferred, bounced, failed,
|
|
218
|
+
rejected, and complained.
|
|
296
219
|
|
|
297
|
-
|
|
298
|
-
and confirm a real queue event updates the intended recipient. Keep a durable
|
|
299
|
-
job worker running and schedule recovery of `prepared` operations that might
|
|
300
|
-
have committed before enqueueing. Monitor stuck operations and failed mailbox jobs.
|
|
220
|
+
## Check your setup
|
|
301
221
|
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
override it. Cloudflare has separate [outgoing size and recipient limits](https://developers.cloudflare.com/email-service/platform/limits/).
|
|
222
|
+
Send to an external inbox, reply back, check the stored attachment, and confirm
|
|
223
|
+
a delivery event updates the intended recipient. Keep Rails jobs and event
|
|
224
|
+
polling running, and monitor failed jobs and the Worker's pending mail.
|
|
306
225
|
|
|
307
|
-
|
|
308
|
-
[
|
|
309
|
-
[the upgrade guide](upgrading-0.3.md) for an existing 0.1 installation.
|
|
226
|
+
Next: [create inboxes](mailboxes.md), [add the management UI](management-engine.md),
|
|
227
|
+
or [troubleshoot missing mail](troubleshooting.md).
|