cloudflare-email 0.1.0 → 0.3.0

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.
Files changed (139) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +149 -1
  3. data/README.md +287 -385
  4. data/SECURITY.md +51 -0
  5. data/app/controllers/cloudflare/email/ingress_controller.rb +33 -21
  6. data/app/controllers/cloudflare/email/management/mailboxes_controller.rb +173 -0
  7. data/app/controllers/cloudflare/email/management/styles_controller.rb +13 -0
  8. data/app/views/cloudflare/email/management/mailboxes/index.html.erb +65 -0
  9. data/app/views/cloudflare/email/management/mailboxes/message.html.erb +34 -0
  10. data/app/views/cloudflare/email/management/mailboxes/show.html.erb +86 -0
  11. data/app/views/layouts/cloudflare/email/management.html.erb +29 -0
  12. data/docs/activerecord-tenanted.md +181 -0
  13. data/docs/architecture.md +105 -0
  14. data/docs/custom-ingress.md +207 -0
  15. data/docs/delivery-events.md +186 -0
  16. data/docs/features.md +138 -0
  17. data/docs/getting-started.md +308 -0
  18. data/docs/mailboxes.md +363 -0
  19. data/docs/management-engine.md +190 -0
  20. data/docs/outbox.md +175 -0
  21. data/docs/routing-deliveries.md +94 -0
  22. data/docs/routing-diagnostics.md +68 -0
  23. data/docs/tenant-mailboxes-plan.md +149 -0
  24. data/docs/thread-correlation.md +27 -0
  25. data/docs/troubleshooting.md +104 -0
  26. data/docs/upgrading-0.2.md +101 -0
  27. data/docs/upgrading-0.3.md +55 -0
  28. data/docs/verification/2026-09-10-followup.md +49 -0
  29. data/docs/verification/2026-09-10-install-upgrade.md +86 -0
  30. data/docs/verification/2026-09-10-live.md +102 -0
  31. data/docs/verification/2026-09-10.md +175 -0
  32. data/docs/verification/2026-09-11-custom-ingress.md +49 -0
  33. data/docs/verification/2026-09-11-extraction.md +32 -0
  34. data/docs/verification/2026-09-11-outbound-ledger.md +60 -0
  35. data/docs/verification/2026-09-11-security.md +76 -0
  36. data/docs/verification/2026-09-11-tenant-mailboxes.md +75 -0
  37. data/docs/verification/2026-09-12-destination-context.md +46 -0
  38. data/docs/verification/2026-09-13-durable-inbound-and-routing.md +71 -0
  39. data/examples/plain_ruby.rb +24 -0
  40. data/lib/cloudflare/email/active_record/base.rb +60 -0
  41. data/lib/cloudflare/email/active_record/delivery_events.rb +72 -0
  42. data/lib/cloudflare/email/active_record/event_inbox.rb +77 -0
  43. data/lib/cloudflare/email/active_record/event_receipt.rb +17 -0
  44. data/lib/cloudflare/email/active_record/mail_snapshot.rb +24 -0
  45. data/lib/cloudflare/email/active_record/outbound_delivery.rb +22 -0
  46. data/lib/cloudflare/email/active_record/outbound_recipient.rb +13 -0
  47. data/lib/cloudflare/email/active_record/outbound_reconciliation.rb +15 -0
  48. data/lib/cloudflare/email/active_record/outbox.rb +219 -0
  49. data/lib/cloudflare/email/active_record/outbox_notifications.rb +34 -0
  50. data/lib/cloudflare/email/active_record/routing_deliveries.rb +128 -0
  51. data/lib/cloudflare/email/active_record.rb +5 -0
  52. data/lib/cloudflare/email/check_route_task.rb +21 -0
  53. data/lib/cloudflare/email/client.rb +93 -18
  54. data/lib/cloudflare/email/consume_events_task.rb +22 -0
  55. data/lib/cloudflare/email/delivery_event.rb +70 -0
  56. data/lib/cloudflare/email/delivery_method.rb +5 -0
  57. data/lib/cloudflare/email/deploy_worker_task.rb +6 -3
  58. data/lib/cloudflare/email/dev_ingress_guard.rb +27 -0
  59. data/lib/cloudflare/email/dev_tunnel.rb +31 -5
  60. data/lib/cloudflare/email/doctor.rb +16 -38
  61. data/lib/cloudflare/email/endpoint.rb +24 -0
  62. data/lib/cloudflare/email/engine.rb +33 -0
  63. data/lib/cloudflare/email/envelope.rb +58 -0
  64. data/lib/cloudflare/email/event_consumer.rb +95 -0
  65. data/lib/cloudflare/email/ingress.rb +115 -0
  66. data/lib/cloudflare/email/mailboxes/configuration.rb +58 -0
  67. data/lib/cloudflare/email/mailboxes/events.rb +112 -0
  68. data/lib/cloudflare/email/mailboxes/inbound_retention.rb +14 -0
  69. data/lib/cloudflare/email/mailboxes/jobs.rb +34 -0
  70. data/lib/cloudflare/email/mailboxes/models.rb +165 -0
  71. data/lib/cloudflare/email/mailboxes/provider_correlation.rb +11 -0
  72. data/lib/cloudflare/email/mailboxes/service.rb +372 -0
  73. data/lib/cloudflare/email/mailboxes/shared_event_receipt.rb +14 -0
  74. data/lib/cloudflare/email/mailboxes.rb +11 -0
  75. data/lib/cloudflare/email/management/adapter.rb +31 -0
  76. data/lib/cloudflare/email/management/configuration.rb +17 -0
  77. data/lib/cloudflare/email/management/engine.rb +15 -0
  78. data/lib/cloudflare/email/management/management.css +68 -0
  79. data/lib/cloudflare/email/management/routes.rb +14 -0
  80. data/lib/cloudflare/email/management.rb +7 -0
  81. data/lib/cloudflare/email/message_id.rb +12 -0
  82. data/lib/cloudflare/email/provider_metadata.rb +77 -0
  83. data/lib/cloudflare/email/provision_catchall_task.rb +4 -4
  84. data/lib/cloudflare/email/provision_route_task.rb +2 -1
  85. data/lib/cloudflare/email/replay_events_job.rb +18 -0
  86. data/lib/cloudflare/email/response.rb +21 -3
  87. data/lib/cloudflare/email/routing_analytics.rb +196 -0
  88. data/lib/cloudflare/email/routing_diagnostics.rb +206 -0
  89. data/lib/cloudflare/email/routing_provisioner.rb +71 -45
  90. data/lib/cloudflare/email/send_job.rb +41 -0
  91. data/lib/cloudflare/email/send_test.rb +5 -29
  92. data/lib/cloudflare/email/signing.rb +2 -2
  93. data/lib/cloudflare/email/tenancy.rb +78 -0
  94. data/lib/cloudflare/email/tenant_job_context.rb +90 -0
  95. data/lib/cloudflare/email/verification.rb +38 -8
  96. data/lib/cloudflare/email/version.rb +1 -1
  97. data/lib/cloudflare/email/worker_deployer.rb +41 -8
  98. data/lib/cloudflare-email.rb +4 -1
  99. data/lib/generators/cloudflare/email/install_generator.rb +53 -37
  100. data/lib/generators/cloudflare/email/mailboxes/catch_all/catch_all_generator.rb +21 -0
  101. data/lib/generators/cloudflare/email/mailboxes/catch_all/templates/add_cloudflare_email_catch_all.rb +18 -0
  102. data/lib/generators/cloudflare/email/mailboxes/mailboxes_generator.rb +55 -0
  103. data/lib/generators/cloudflare/email/mailboxes/templates/create_cloudflare_email_mailboxes.rb +58 -0
  104. data/lib/generators/cloudflare/email/mailboxes/templates/create_cloudflare_email_receiving_domains.rb +16 -0
  105. data/lib/generators/cloudflare/email/mailboxes/templates/create_cloudflare_email_shared_events.rb +35 -0
  106. data/lib/generators/cloudflare/email/outbox/outbox_generator.rb +28 -0
  107. data/lib/generators/cloudflare/email/outbox/templates/create_cloudflare_email_outbox.rb +48 -0
  108. data/lib/generators/cloudflare/email/routing_tracking/routing_tracking_generator.rb +28 -0
  109. data/lib/generators/cloudflare/email/routing_tracking/templates/create_cloudflare_email_routing_delivery_receipts.rb +22 -0
  110. data/lib/generators/cloudflare/email/templates/initializer.rb +3 -3
  111. data/lib/generators/cloudflare/email/templates/main_mailbox.rb +2 -7
  112. data/lib/generators/cloudflare/email/tracking/templates/create_cloudflare_email_event_receipts.rb +22 -0
  113. data/lib/generators/cloudflare/email/tracking/tracking_generator.rb +26 -0
  114. data/lib/tasks/cloudflare_email.rake +42 -4
  115. data/lib/tasks/cloudflare_email_diagnostics.rake +11 -0
  116. data/templates/deploy-to-cloudflare/.dev.vars.example +3 -0
  117. data/templates/deploy-to-cloudflare/.gitignore +6 -0
  118. data/templates/deploy-to-cloudflare/LICENSE.txt +21 -0
  119. data/templates/deploy-to-cloudflare/README.md +84 -0
  120. data/templates/deploy-to-cloudflare/package-lock.json +2627 -0
  121. data/templates/deploy-to-cloudflare/package.json +36 -0
  122. data/templates/deploy-to-cloudflare/src/index.js +445 -0
  123. data/templates/deploy-to-cloudflare/test/durable.test.ts +193 -0
  124. data/templates/deploy-to-cloudflare/test/index.test.ts +309 -0
  125. data/templates/deploy-to-cloudflare/test/relay.test.ts +226 -0
  126. data/templates/deploy-to-cloudflare/vitest.config.ts +8 -0
  127. data/templates/deploy-to-cloudflare/wrangler.toml +22 -0
  128. data/templates/worker/README.md +231 -16
  129. data/templates/worker/docs/durable-inbound.md +149 -0
  130. data/templates/worker/package-lock.json +2627 -0
  131. data/templates/worker/package.json +8 -5
  132. data/templates/worker/scripts/wrangler.mjs +16 -0
  133. data/templates/worker/src/index.js +397 -25
  134. data/templates/worker/test/durable.test.ts +193 -0
  135. data/templates/worker/test/index.test.ts +183 -13
  136. data/templates/worker/test/relay.test.ts +226 -0
  137. data/templates/worker/wrangler.toml +63 -4
  138. metadata +154 -7
  139. data/lib/cloudflare/email/secure_message_id.rb +0 -89
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0f26340bc2629fee65067ec40a1f78ffea475e70a7d102931f3a96ab0f65d621
4
- data.tar.gz: 291281b738050c94bc80062a977f0b820178b70e4574f49ed449d737c06aa70c
3
+ metadata.gz: b8b7e8e8e9c8f102cf640de5aa577092947f829853daea59463263b8729ede21
4
+ data.tar.gz: 7303add7cb750e309c338132e114ecd40340bf5e9ee39b6b03c3f28acd9887d1
5
5
  SHA512:
6
- metadata.gz: 62b1afce0f1b6afaa33091a1c9540702f7d09e7b1ecb3a3cd71b11a70fbd52162aeb50dba1696fb6a23799096d88be0a1d06ff804b996191018d4641ca5c68a4
7
- data.tar.gz: 2e2210138612fb118ae196f9c97569e3006e6d6f43657efa5be80e5e7636bd55bb112742aaf08df092d4668d66295fa6a5e35df9e9035946897db99ad1c237bc
6
+ metadata.gz: 77cbd0de1b51f621f834fa9493c31865cc0017e0f221885503ad6c891f783f39b3e0eaf07eaabb148a9e34c694bed9d8e4a0dfa27cd052e70e0d9e17164e9483
7
+ data.tar.gz: 3e3ee8aa24120e3b264efd7328b18dc3c48ab044656c0e15d0f73a13d2fd18e06247ab7392bc72aba472d37bee1b080a77113e492d4dcbe8c6945e2c5ceeb34a
data/CHANGELOG.md CHANGED
@@ -1,6 +1,154 @@
1
1
  # Changelog
2
2
 
3
- ## Unreleased
3
+ ## 0.3.0 — 2026-09-13
4
+
5
+ See [upgrading from 0.2](docs/upgrading-0.3.md) before deploying the updated Worker.
6
+
7
+ - Add a standalone Deploy to Cloudflare template with R2/Queue provisioning,
8
+ secret prompts, scheduled recovery, and logs. CI checks runtime/configuration
9
+ synchronization with the gem's Worker and tests an isolated template copy.
10
+
11
+ - Make durable inbound delivery the Worker default, with explicit direct fallback: commit original mail and envelope context
12
+ to R2 before Worker acceptance, deliver queue pointers with fresh signatures,
13
+ and recover pending mail through a bounded scheduled sweep. Rails errors,
14
+ lost responses, enqueue failures and exhausted queue retries retain pending
15
+ mail. Provisioning, protected storage and monitoring remain explicit.
16
+ - Make missing Message-ID fallback deterministic across Rails hosts so replay
17
+ after a deployment does not create another inbound record. Original MIME and
18
+ parseable provider Message-IDs remain unchanged.
19
+ - Add optional authenticated Routing analytics delivery confirmation with
20
+ encrypted immutable receipts, strict single-envelope-recipient correlation,
21
+ replay and transactional callbacks. Sending acceptance remains unchanged;
22
+ missing or ambiguous evidence never authorizes a resend.
23
+ - Bound shared sending/queue HTTP responses by streamed bytes and a total
24
+ per-attempt deadline; disable implicit HTTP retries and sanitize transport
25
+ failures. Sending and queue acknowledgement ambiguity stays safe to replay
26
+ through durable receipts rather than resending accepted email.
27
+ - Document verified Routing destinations, separate analytics credentials and
28
+ explicit system-mail storage with optional SQLite tenancy.
29
+
30
+ - Add optional domain catch-all receiving on an existing active mailbox address,
31
+ with explicit verification evidence and a unique active catch-all constraint.
32
+ Exact registered addresses always win, including rejected inactive addresses;
33
+ unknown local parts retain their actual envelope recipient without new aliases.
34
+ Destination snapshots identify fallback, while sending stays exact-address only.
35
+ A separate generator upgrades existing mailbox schemas; catch-all is never
36
+ enabled automatically.
37
+
38
+ - Allow production Rails schema preloading to boot without a selected tenant.
39
+ Tenant model pool access still fails closed with
40
+ `Cloudflare::Email::ActiveRecord::TenantConnectionUnavailable`, an
41
+ `ActiveRecord::ConnectionNotEstablished` error retaining the original
42
+ `ConfigurationError` as its cause. No default tenant is selected.
43
+
44
+ - Add a reusable Worker `relayEmail` pipeline with host backend/header policies,
45
+ bounded raw reads, optional fail-open archive hooks, safe HTTP outcomes and
46
+ deadlines. `archiveEmail` writes raw bytes and envelope metadata to an optional
47
+ R2 bucket. Default and custom forwarding share the same HTTP transport.
48
+ - Keep provider authentication interpretation, archive keys, retry decisions and
49
+ fallback destinations with the host; existing default v2/v3 forwarding remains
50
+ compatible. Archive deadlines bound waiting but cannot cancel an in-flight write.
51
+
52
+ - Add `Mailboxes.with_recipient` for active-address resolution and immutable
53
+ destination context with host-owned persistence, without ActionMailbox.
54
+ - Yield the same destination from `Mailboxes.receive` so apps can link their
55
+ business records while retaining gem-managed raw email, metadata and membership
56
+ persistence. Host exceptions propagate instead of being reported as missing
57
+ mailbox records; existing no-argument receive blocks continue to work.
58
+
59
+ - Add a bounded, Rails-independent `Ingress.verify` API for existing ingestion
60
+ pipelines, with optional ActionMailbox persistence and tenant registry routing.
61
+ - Add opt-in v3 signatures for provider metadata supplied by custom Workers.
62
+ Metadata is authenticated transport evidence, not proof of sender authenticity.
63
+ Default Worker forwarding remains v2; upgrade Rails before enabling v3.
64
+ - Verify signatures against exact timestamp header bytes; decimal parsing is
65
+ used only for freshness. The bundled Worker's timestamp format is unchanged.
66
+ - Add GET-only `cloudflare:email:check_route` diagnostics for exact receiving-domain
67
+ DNS and Worker routing. Reports uncertainty without changing configuration or
68
+ claiming live delivery is verified.
69
+
70
+ - Add an opt-in server-rendered mailbox management engine with host authentication,
71
+ mailbox/domain scoping and per-action permissions. Manage mailboxes and aliases,
72
+ suspend/resume, preview plain-text messages and mark read/archive without
73
+ JavaScript or asset pipeline dependencies. The engine is never mounted by default.
74
+
75
+ - Fix managed domain and address activation in Rails apps with strict readonly
76
+ attributes enabled. Normalize immutable identities only when creating records;
77
+ later lifecycle updates retain the original domain and address.
78
+
79
+ ## 0.2.0 — 2026-09-11
80
+
81
+ - Add optional managed mailboxes: domain directory, addresses/aliases, lifecycle,
82
+ incoming memberships, read/archive/purge APIs and raw-mail retention.
83
+ - Add explicit tenant connections, tenant-aware framework and mailbox jobs,
84
+ mailbox-authorized outbox sends, shared event intake/correlation and recovery.
85
+ Verify separate SQLite databases and actual `activerecord-tenanted` integration;
86
+ no new runtime dependency for plain Ruby consumers.
87
+ - Add a mailbox generator with separate shared/tenant migration paths and
88
+ user guides for mailbox management and database tenancy.
89
+
90
+ - Bound Rails and Worker inbound reads to 25 MiB by default; reject malformed or
91
+ stale signing headers before reading MIME. Require HTTPS remote endpoints.
92
+ - Restrict the development tunnel to email ingress and require a running guard;
93
+ redact client inspection, retry logs, and generated mailbox logging.
94
+ - Validate delivery-event schemas, preserve receipt evidence through ordinary
95
+ ActiveRecord updates, and tighten provider acceptance response handling.
96
+ - Remove Rails 7.1 support, update patched Rails/SQLite test floors, and audit
97
+ resolved Ruby dependencies in CI. See the dated security verification report.
98
+
99
+ - Add an optional durable Rails outbox: immutable MIME/envelope snapshots,
100
+ account-scoped operation keys, committed send claims, per-recipient acceptance
101
+ evidence, blocked uncertain retries, and audited subset reconciliation.
102
+ - Add `Outbox.prepare_mail`, identity-only `SendJob`, replay jobs/tasks and
103
+ `DeliveryEvents` to correlate provider events and update recipient lifecycle
104
+ state transactionally. Normalize receipt message IDs for indexed replay.
105
+ - Verify concurrent sends and event projection on PostgreSQL as well as SQLite;
106
+ include process termination, partial outcomes and post-acceptance persistence
107
+ failures. The inbox uses the shared ledger with preserved historical records.
108
+
109
+ - Authenticate SMTP envelope sender/recipient with the Worker's v2 HMAC format,
110
+ persist trusted metadata before routing, and expose `Envelope.for(inbound_email)`.
111
+ Preserve raw MIME and scope duplicate detection to the exact SMTP recipient.
112
+ Require v2 ingress; remove v1 compatibility and the unused signed Message-ID
113
+ helper. Coordinate Rails and Worker deployment; see docs/upgrading-0.2.md.
114
+
115
+ - Extract shared provider acceptance, delivery-event ordering, and Message-ID
116
+ normalization primitives from the inbox application.
117
+ - Add an opt-in ActiveRecord event inbox and `cloudflare:email:tracking` generator:
118
+ committed receipts before ACK, account/event uniqueness, payload-conflict
119
+ detection, unmatched replay, and transactional handlers with explicit outcomes.
120
+
121
+ - Verify actual local workerd-to-Rails delivery in CI. This exposed and fixed an
122
+ unsupported fetch redirect mode; use `manual` and reject the 3xx response.
123
+ Add reusable package/fresh-install/task verification and a dated evidence report.
124
+
125
+ - Add outbound delivery events through Cloudflare Queues HTTP pull, typed event
126
+ data, account/domain checks, per-message acknowledgement after handler success,
127
+ and a Rails `consume_events` task. Optional Rails receipts provide durable
128
+ deduplication; applications supply correlation and processing policy.
129
+ - Expose suppressed recipients and provider message IDs in responses/notifications;
130
+ support cc/bcc-only structured sends and HTTP-date Retry-After headers.
131
+ - Default to retrying only rate limits and pre-send connection failures. Ambiguous
132
+ network/5xx retries now require `retry_ambiguous: true` to accept duplication risk.
133
+ - Fix generated ENV credentials, send-only eager boot, duplicate ingress responses,
134
+ accidental generator deployment tasks, and development environment isolation.
135
+ - Require subdomain routing DNS preflight; refuse misleading subdomain catch-all
136
+ scope; use current apex DNS API and paginated rule lookup; surface setup failures.
137
+ - Update locked Worker tooling, require explicit Wrangler environments, bound
138
+ forwarding requests to 15 seconds, and reject redirects. Add scheduled CI,
139
+ dependency checks, actual Rails integration tests, and Ruby 4 / Rails 8.1 coverage.
140
+ - Require FROM for test sends; remove undocumented sending-domain discovery and
141
+ misleading diagnostic claims. Update DNS, SMTP, retry, and signed-ID guidance.
142
+ - Bound legacy signed Message-ID output to 900 bytes; reject empty decode secrets.
143
+ Live Cloudflare testing confirmed custom IDs are replaced; use provider IDs
144
+ for correlation, not sender authentication. See docs/upgrading-0.2.md.
145
+ - Accept plain JSON queue bodies returned by live Email Sending subscriptions,
146
+ retaining Base64 compatibility. Verify real delivery-event redelivery and ack.
147
+
148
+ ## 0.1.0 — 2026-04-18
149
+
150
+ The notes below describe the original April implementation and its historical
151
+ verification. Current compatibility and security guidance is in the 0.2.0 docs.
4
152
 
5
153
  - **`Cloudflare::Email::SecureMessageId`** — sign the outbound `Message-ID:`
6
154
  with HMAC-SHA256. The recipient's reply naturally carries the signed id