@kurtbruns/kestrel 1.18.0 → 1.19.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.
- package/.dev.vars.example +16 -4
- package/CHANGELOG.md +44 -1
- package/dist/public/dashboard/app.js +303 -305
- package/dist/public/dashboard/app.js.map +4 -4
- package/dist/public/dashboard/index.html +2 -2
- package/dist/public/dashboard/styles.css +123 -21
- package/dist/worker/index.js +3507 -2512
- package/docs/get-started/02-deploy.md +1 -1
- package/docs/get-started/03-access.md +11 -3
- package/docs/get-started/05-ses.md +47 -28
- package/docs/get-started/06-production-access.md +6 -3
- package/docs/get-started/07-verify.md +2 -2
- package/docs/get-started/08-go-live.md +3 -1
- package/docs/guides/01-allow-claude.md +1 -1
- package/docs/guides/03-notifications.md +10 -10
- package/docs/publishing/06-troubleshooting.md +26 -2
- package/docs/publishing/08-connect-claude.md +1 -1
- package/docs/publishing/09-share-your-subscribe-link.md +3 -1
- package/docs/publishing/10-check-your-setup.md +13 -9
- package/docs/publishing/README.md +8 -3
- package/docs/reference/01-configuration.md +5 -5
- package/migrations/0016_held_receipts.sql +26 -0
- package/migrations/0017_provider_account_rejected.sql +24 -0
- package/migrations/0018_notify_addresses.sql +13 -0
- package/migrations/0019_sending.sql +67 -0
- package/migrations/0020_notifications_not_sending.sql +40 -0
- package/package.json +1 -1
- package/packages/kestrel/lib/api.mjs +28 -3
- package/packages/kestrel/lib/oauth.mjs +5 -3
- package/template/claude/CLAUDE.md +3 -1
- package/template/wrangler.jsonc +5 -5
|
@@ -209,7 +209,7 @@ npx wrangler delete --name newsletter-dev
|
|
|
209
209
|
|
|
210
210
|
In a browser, `https://newsletter.example.com/dashboard/` says the editor is locked until you set up Access. That's expected.
|
|
211
211
|
|
|
212
|
-
1. In a browser, `https://newsletter.example.com/` shows the newsletter's public home page. Its subscribe page, `/subscribe`, says signups aren't open yet, until Connect Amazon SES.
|
|
212
|
+
1. In a browser, `https://newsletter.example.com/` shows the newsletter's public home page. Its subscribe page, `/subscribe`, says signups aren't open yet, until Connect Amazon SES has sent its first test email.
|
|
213
213
|
|
|
214
214
|
If the public pages instead say the newsletter is temporarily unavailable, and `/health` answers `500` with a body like `{"error":"invalid_config","variable":"APP_ORIGIN",…}`, the setting it names is missing, malformed, or still on `example.com`. Fix it in `wrangler.jsonc`, commit, and deploy again.
|
|
215
215
|
|
|
@@ -79,13 +79,21 @@ The app verifies each login against your team domain, and checks that it was iss
|
|
|
79
79
|
|
|
80
80
|
A secret takes effect as soon as you store it, with no redeploy.
|
|
81
81
|
|
|
82
|
-
1. **(Optional, recommended)** Limit the editor to named people. The Access policy decides who can sign in. This list keeps that true if the policy is widened later, for another app or by mistake.
|
|
82
|
+
1. **(Optional, recommended)** Limit the editor to named people. The Access policy decides who can sign in. This list keeps that true if the policy is widened later, for another app or by mistake. Store the list of addresses allowed in:
|
|
83
83
|
|
|
84
84
|
```bash
|
|
85
85
|
npx wrangler secret put ACCESS_ALLOWED_EMAILS --env production
|
|
86
86
|
```
|
|
87
87
|
|
|
88
|
-
|
|
88
|
+
At the prompt, enter the addresses separated by commas:
|
|
89
|
+
|
|
90
|
+
```
|
|
91
|
+
you@example.com, publisher@example.com
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
Spaces and capital letters don't matter. Without the list, the app lets in anyone your Access policy allows.
|
|
95
|
+
|
|
96
|
+
To let someone else into the editor, such as the publisher or a co-editor, add their email address to the policy from [Allow yourself in](#3-allow-yourself-in). If you set `ACCESS_ALLOWED_EMAILS`, run the command again and enter the whole list with their address added, since the new value replaces the old one.
|
|
89
97
|
|
|
90
98
|
## Check it
|
|
91
99
|
|
|
@@ -112,7 +120,7 @@ The app verifies each login against your team domain, and checks that it was iss
|
|
|
112
120
|
If a check fails:
|
|
113
121
|
|
|
114
122
|
- **The editor says the configuration needs a fix.** Store the secret it names. While only one of the two is stored, `/health` answers `500` naming the other, and the public pages say the newsletter is temporarily unavailable.
|
|
115
|
-
- **Cloudflare lets you in, but the editor answers `401`.** Check the two secrets for a typo, and that `ACCESS_ALLOWED_EMAILS`, if you set it, includes your address.
|
|
123
|
+
- **Cloudflare lets you in, but the editor answers `401`.** Check the two secrets for a typo, and that `ACCESS_ALLOWED_EMAILS`, if you set it, includes your address. The same goes for anyone else you let in.
|
|
116
124
|
- **The editor says Access let you in but didn't say who you are.** Your login carried no email, which happens through a kind of policy the app doesn't accept, such as a device check. Sign out, and sign in through the policy you made above.
|
|
117
125
|
|
|
118
126
|
Next, [create an AWS account](04-aws-account.md).
|
|
@@ -19,7 +19,7 @@ The same form sets a custom MAIL FROM domain, so SPF also passes for your domain
|
|
|
19
19
|
|
|
20
20
|
SES keeps identities, quotas, and the sandbox separately in each region, so do every step on this page in the region you chose. [Configuration](../reference/01-configuration.md#vars) says more about choosing one, under `AWS_REGION`.
|
|
21
21
|
|
|
22
|
-
1. Note the region's code, such as `
|
|
22
|
+
1. Note the region's code, such as `us-west-2`. The menu shows it beside the region's name, and [Amazon SES endpoints](https://docs.aws.amazon.com/general/latest/gr/ses.html) lists each one too. AWS names a region in more than one way, such as United States (Oregon) and US West (Oregon), so go by the code. Later steps use it.
|
|
23
23
|
|
|
24
24
|
1. Under **Configuration**, choose **Identities**, then **Create identity**.
|
|
25
25
|
|
|
@@ -73,27 +73,31 @@ A new SES account starts in the sandbox: it sends only to addresses you've verif
|
|
|
73
73
|
|
|
74
74
|
1. Choose two addresses you own, such as a personal address and a work address. Verify it works uses both.
|
|
75
75
|
|
|
76
|
-
1. For each, under **Identities**, choose **Create identity**, select **Email address**, and enter it. Choose **Create identity**.
|
|
76
|
+
1. For each, under **Identities**, choose **Create identity**, select **Email address**, and enter it. Leave **Assign a default configuration set** and **Assign to a tenant** unchecked. The form's notice about DMARC is about sending from the address, and the app only sends to it. Choose **Create identity**.
|
|
77
77
|
|
|
78
78
|
1. Open the email AWS sends to each address, and follow its link. The identity's status changes to **Verified**.
|
|
79
79
|
|
|
80
|
+
AWS's email and confirmation page say you can now send from the address. That's expected: SES has one kind of verified address, and the same verification lets the sandbox send to it.
|
|
81
|
+
|
|
80
82
|
## 4. Send bounces and complaints to the app
|
|
81
83
|
|
|
82
84
|
SES reports bounces and complaints through Amazon Simple Notification Service (SNS), which delivers them to the app. The app then stops mailing an address that bounced or complained. A configuration set says which events SES reports, and an SNS topic carries them.
|
|
83
85
|
|
|
84
|
-
1. In SES, under **Configuration**, choose **Configuration sets**, then **Create set**. Name it `kestrel-mail`, and choose **Create set**.
|
|
86
|
+
1. In SES, under **Configuration**, choose **Configuration sets**, then **Create set**. Name it `kestrel-mail`, keep every other option as it is, and choose **Create set**.
|
|
85
87
|
|
|
86
88
|
1. On the set's **Event destinations** tab, choose **Add destination**. Select **Hard bounces**, **Complaints**, **Deliveries**, and **Rendering failures**, and choose **Next**. The app logs a rendering failure, an email SES couldn't build, as an error.
|
|
87
89
|
|
|
88
|
-
|
|
90
|
+
Leave the other six off, since the app already knows about them or doesn't act on them. **Sends** repeats what the send records, and **Delivery delays** end as a bounce or a delivery. **Rejects** (a virus) and **Subscriptions** (SES's own unsubscribe) don't arise, since the app's emails carry no attachments and their own unsubscribe link. **Opens** and **Clicks** would add tracking the app doesn't record.
|
|
91
|
+
|
|
92
|
+
1. For **Destination type**, select **Amazon SNS**. Name it `kestrel-sns`, and keep **Event publishing** enabled.
|
|
89
93
|
|
|
90
|
-
1.
|
|
94
|
+
1. On the same screen, under **SNS topic**, choose **Create SNS topic**. Name it `kestrel-events`, leave the display name empty, and choose **Create topic**. Then select `kestrel-events` in the topic menu, if it isn't already. Choose **Next**, then **Add destination**.
|
|
91
95
|
|
|
92
|
-
1. Make the set your hostname's default, so SES reports on every email sent from it.
|
|
96
|
+
1. Make the set your hostname's default, so SES reports on every email sent from it. Under **Identities**, open `send.example.com`, and choose its **Configuration set** tab, below the summary. Edit the **Default configuration set**, select **Assign a default configuration set**, select `kestrel-mail`, and save.
|
|
93
97
|
|
|
94
98
|
1. Open the topic in the [SNS console](https://console.aws.amazon.com/sns/), in the same region, and find its ARN. It looks like `arn:aws:sns:us-east-1:111122223333:kestrel-events`, where `us-east-1` is your region's code and `111122223333` is your account ID.
|
|
95
99
|
|
|
96
|
-
1.
|
|
100
|
+
1. Check that SES may publish to the topic, on its **Access policy** tab. A topic created from SES's **Add destination**, as above, already lets SES publish to it from your account. Leave that statement as it is. For a topic you made in the SNS console instead, choose **Edit**, expand **Access policy**, and add this statement to the `Statement` list:
|
|
97
101
|
|
|
98
102
|
```json
|
|
99
103
|
{
|
|
@@ -126,15 +130,13 @@ SES reports bounces and complaints through Amazon Simple Notification Service (S
|
|
|
126
130
|
|
|
127
131
|
## 5. Create the app's AWS key
|
|
128
132
|
|
|
129
|
-
The app sends through an AWS Identity and Access Management (IAM) user whose key can only send email and read the account's sending status. That limits the harm if the key ever leaks. Reading the status lets the app show whether SES is still in the sandbox
|
|
133
|
+
The app sends through an AWS Identity and Access Management (IAM) user whose key can only send email and read the account's sending status. That limits the harm if the key ever leaks. Reading the status lets the app show whether SES is still in the sandbox and take its send rate from the account. It also warns, without sending anything, when the configuration set is missing or paused, the sender isn't verified, or bounces and complaints can't reach the app.
|
|
130
134
|
|
|
131
|
-
If `kestrel-sender` already exists,
|
|
135
|
+
If `kestrel-sender` already exists, from an earlier release, don't create a second user or key. Update its policy instead, as the end of this section shows.
|
|
132
136
|
|
|
133
|
-
1. In the [IAM console](https://console.aws.amazon.com/iam/),
|
|
137
|
+
1. In the [IAM console](https://console.aws.amazon.com/iam/), choose **Policies**, then **Create policy**.
|
|
134
138
|
|
|
135
|
-
1.
|
|
136
|
-
|
|
137
|
-
1. In the new tab, choose the **JSON** editor, and replace its contents with this policy:
|
|
139
|
+
1. Choose the **JSON** editor, and replace its contents with this policy:
|
|
138
140
|
|
|
139
141
|
```json
|
|
140
142
|
{
|
|
@@ -145,8 +147,11 @@ If `kestrel-sender` already exists, as on an instance set up before Kestrel read
|
|
|
145
147
|
"Action": [
|
|
146
148
|
"ses:SendEmail",
|
|
147
149
|
"ses:SendBulkEmail",
|
|
150
|
+
"ses:SendTemplatedEmail",
|
|
151
|
+
"ses:SendBulkTemplatedEmail",
|
|
148
152
|
"ses:GetAccount",
|
|
149
|
-
"ses:GetEmailIdentity"
|
|
153
|
+
"ses:GetEmailIdentity",
|
|
154
|
+
"ses:GetConfigurationSet"
|
|
150
155
|
],
|
|
151
156
|
"Resource": "*"
|
|
152
157
|
}
|
|
@@ -154,13 +159,17 @@ If `kestrel-sender` already exists, as on an instance set up before Kestrel read
|
|
|
154
159
|
}
|
|
155
160
|
```
|
|
156
161
|
|
|
157
|
-
|
|
162
|
+
1. Choose **Next**, name the policy `kestrel-send-email`, and choose **Create policy**. The review lists Pinpoint Email, SES, and SES v2, which is how IAM shows these SES actions.
|
|
163
|
+
|
|
164
|
+
1. Choose **IAM users**, then **Create user**. Name it `kestrel-sender`, leave console access off, and choose **Next**.
|
|
158
165
|
|
|
159
|
-
1.
|
|
166
|
+
1. Choose **Attach policies directly**, search for `kestrel-send-email`, and select it. AWS recommends groups, which help manage many people, and one app's user doesn't need one. Choose **Next**, then **Create user**.
|
|
160
167
|
|
|
161
|
-
1.
|
|
168
|
+
1. Open `kestrel-sender`, and choose **Create access key**. It's an access key, not an API key. For the use case, choose **Application running outside AWS**, and choose **Next**.
|
|
162
169
|
|
|
163
|
-
|
|
170
|
+
AWS suggests IAM Roles Anywhere instead. It needs a certificate authority of your own, so use the access key.
|
|
171
|
+
|
|
172
|
+
1. For the description tag, enter the app's hostname, such as `newsletter.example.com`, so you can tell its key apart later. Choose **Create access key**.
|
|
164
173
|
|
|
165
174
|
1. Keep the page open: AWS shows the secret access key only once. Store the access key ID in the app, and paste it at the prompt:
|
|
166
175
|
|
|
@@ -174,6 +183,8 @@ If `kestrel-sender` already exists, as on an instance set up before Kestrel read
|
|
|
174
183
|
npx wrangler secret put AWS_SECRET_ACCESS_KEY --env production
|
|
175
184
|
```
|
|
176
185
|
|
|
186
|
+
To update an existing key's policy, open **Policies** in the IAM console, then `kestrel-send-email`, and choose **Edit**. In the JSON editor, make its `Action` list match the policy above, and save. Without `ses:SendTemplatedEmail`, SES refuses even a test email. Once the key may call `ses:GetAccount`, the app's next check, within an hour, reads the account. Without `ses:GetConfigurationSet`, the app can't tell whether the configuration set exists, so it says nothing about it rather than guessing.
|
|
187
|
+
|
|
177
188
|
## 6. Connect the app and deploy
|
|
178
189
|
|
|
179
190
|
Until now the app has run with no email provider connected, refusing to send. Setting `PROVIDER` to `ses` connects SES, with the secrets you stored in sections 4 and 5. If a required one is missing, the app refuses to run until you store it, and `/health` names it.
|
|
@@ -222,6 +233,8 @@ Until now the app has run with no email provider connected, refusing to send. Se
|
|
|
222
233
|
|
|
223
234
|
1. In the editor, **Settings → Email sender** shows Amazon SES and your From address, where it said **No email provider connected** before. Within a couple of minutes, a **Sandbox** pill appears beside it, once the app has read your SES account.
|
|
224
235
|
|
|
236
|
+
1. In the editor, open **Template** and select **Send test email**, to an address you verified in [Verify your test addresses](#3-verify-your-test-addresses). It arrives. Signups stay closed until SES takes an email, so the **Not proven yet** pill beside Amazon SES in **Settings → Email sender** is gone once it has. If SES refuses it, the test's line says why and what fixes it.
|
|
237
|
+
|
|
225
238
|
1. In a browser, `https://newsletter.example.com/subscribe` shows the subscribe form, where it said signups aren't open yet.
|
|
226
239
|
|
|
227
240
|
## 7. Subscribe the app to the topic
|
|
@@ -240,7 +253,7 @@ SNS delivers events only to a subscriber that has confirmed. The app confirms on
|
|
|
240
253
|
|
|
241
254
|
Any other answer, such as a redirect to your Cloudflare login, means Access covers the webhook. Remove that path from your Access application.
|
|
242
255
|
|
|
243
|
-
1. In the SNS console, open your topic, and choose **Create subscription**.
|
|
256
|
+
1. In the SNS console, open your topic, and choose **Create subscription**. Check that **Topic ARN** shows the `kestrel-events` topic, and choose it if it's empty.
|
|
244
257
|
|
|
245
258
|
1. Set **Protocol** to **HTTPS**, and enter the endpoint:
|
|
246
259
|
|
|
@@ -248,13 +261,9 @@ SNS delivers events only to a subscriber that has confirmed. The app confirms on
|
|
|
248
261
|
https://newsletter.example.com/webhooks/ses
|
|
249
262
|
```
|
|
250
263
|
|
|
251
|
-
1. Leave raw message delivery off
|
|
252
|
-
|
|
253
|
-
1. Reload the subscription. Its status changes to **Confirmed**, with nothing for you to click.
|
|
254
|
-
|
|
255
|
-
If it stays **Pending confirmation**, check that your Access application covers only `/dashboard` and `/api`, that `SNS_TOPIC_ARN` matches the topic's ARN, and that section 6's deploy finished. A mismatched ARN shows in the app's logs as a `webhook.refused` error naming both. Then request confirmation again from the topic's subscriptions. SNS doesn't let you delete a pending subscription, and removes it on its own after 48 hours.
|
|
264
|
+
1. Leave raw message delivery off.
|
|
256
265
|
|
|
257
|
-
1. Give SNS longer to retry. By default it retries a failed delivery 3 times over about a minute, then discards the event, so a bounce that arrives mid-deploy is lost.
|
|
266
|
+
1. Give SNS longer to retry. By default it retries a failed delivery 3 times over about a minute, then discards the event, so a bounce that arrives mid-deploy is lost. Expand **Delivery policy (HTTP/S)**, turn off **Use the default delivery policy**, and set these values:
|
|
258
267
|
|
|
259
268
|
| Field | Value |
|
|
260
269
|
| --- | --- |
|
|
@@ -266,13 +275,19 @@ SNS delivers events only to a subscriber that has confirmed. The app confirms on
|
|
|
266
275
|
| Maximum delay retries | `6` |
|
|
267
276
|
| Retry-backoff function | Exponential |
|
|
268
277
|
|
|
269
|
-
SNS then keeps trying for about 40 minutes. It allows an hour at most.
|
|
278
|
+
SNS then keeps trying for about 40 minutes. It allows an hour at most. The topic has a delivery policy of its own, under **Edit**. Setting it there instead also covers any subscription you add later.
|
|
279
|
+
|
|
280
|
+
1. Choose **Create subscription**.
|
|
281
|
+
|
|
282
|
+
1. Reload the subscription. Its status changes to **Confirmed**, with nothing for you to click.
|
|
283
|
+
|
|
284
|
+
If it stays **Pending confirmation**, check that your Access application covers only `/dashboard` and `/api`. Check that `SNS_TOPIC_ARN` matches the topic's ARN, and that section 6's deploy finished. A mismatched ARN shows in the app's logs as a `webhook.refused` error naming both. Then request confirmation again from the topic's subscriptions. SNS doesn't let you delete a pending subscription, and removes it on its own after 48 hours.
|
|
270
285
|
|
|
271
286
|
1. **(Optional)** Keep what SNS still can't deliver. On the subscription, choose **Edit**, and set a **Redrive policy (dead-letter queue)** to an SQS queue of your own. SNS then moves an event there instead of discarding it. [Amazon SNS dead-letter queues](https://docs.aws.amazon.com/sns/latest/dg/sns-dead-letter-queues.html) covers the queue and its permissions.
|
|
272
287
|
|
|
273
288
|
## 8. Receive replies at your From address
|
|
274
289
|
|
|
275
|
-
Replies to a post go to your From address, `newsletter@send.example.com`, which has no inbox yet, so they bounce. This section forwards that address to the publisher's inbox with Cloudflare Email Routing. Forwarding only receives mail, so it doesn't change how SES sends.
|
|
290
|
+
Replies to a post go to your From address, `newsletter@send.example.com`, which has no inbox yet, so they bounce. The reader is told your domain couldn't be found. This section forwards that address to the publisher's inbox with Cloudflare Email Routing. Forwarding only receives mail, so it doesn't change how SES sends.
|
|
276
291
|
|
|
277
292
|
1. Check that your From address has no inbox yet. This prints nothing:
|
|
278
293
|
|
|
@@ -286,7 +301,7 @@ Replies to a post go to your From address, `newsletter@send.example.com`, which
|
|
|
286
301
|
|
|
287
302
|
1. Add `send.example.com` to Email Routing: select `example.com`, then **Settings**, and add `send` under **Subdomains**.
|
|
288
303
|
|
|
289
|
-
1. Cloudflare adds MX, SPF, and DKIM records for `send.example.com`, and locks them. Wait until they show as added.
|
|
304
|
+
1. Cloudflare adds MX, SPF, and DKIM records for `send.example.com`, and locks them. Wait until they show as added. Its SPF record is for forwarding: SES's mail passes SPF at `bounce.send.example.com`, from section 1.
|
|
290
305
|
|
|
291
306
|
1. Under **Destination Addresses**, add the publisher's inbox. Open the email Cloudflare sends to it, and select **Verify email address**. If it isn't your inbox, ask the publisher to do this step.
|
|
292
307
|
|
|
@@ -300,6 +315,10 @@ Replies to a post go to your From address, `newsletter@send.example.com`, which
|
|
|
300
315
|
dig +short MX send.example.com
|
|
301
316
|
```
|
|
302
317
|
|
|
318
|
+
1. Send an email to `newsletter@send.example.com` from an address whose mail doesn't go through the publisher's inbox. Gmail, for one, hides a forwarded copy of mail you sent yourself. In Email Routing's **Activity log**, the email shows as **Forwarded**.
|
|
319
|
+
|
|
320
|
+
If it bounces saying the domain couldn't be found, the sender's provider is still remembering that `send.example.com` had no mail servers. That happens after an email to it before this section, and lasts up to 30 minutes on Cloudflare's DNS. Wait, and send it again.
|
|
321
|
+
|
|
303
322
|
Out-of-office and other automatic replies reach the publisher's inbox too. Bounces don't: they go to SES, which reports them to the app. The publisher answers from their own inbox, not from `newsletter@send.example.com`. To answer from the newsletter's address instead, give it a mailbox at a mail host such as [Fastmail](https://www.fastmail.help/hc/en-us/articles/360060591133) or [Google Workspace](https://support.google.com/a/answer/140034), in place of forwarding.
|
|
304
323
|
|
|
305
324
|
SES is now connected in the sandbox. Until AWS grants production access, it sends only to the addresses you verified in section 3. It also sends to the SES test addresses Verify it works uses. SES charges for each email it sends, test emails included, as [Amazon SES pricing](https://aws.amazon.com/ses/pricing/) lists.
|
|
@@ -34,11 +34,13 @@ AWS reviews each request by hand, for the region the app sends from.
|
|
|
34
34
|
|
|
35
35
|
1. Check the acknowledgement, and select **Submit request**. AWS Support answers by email.
|
|
36
36
|
|
|
37
|
-
1. Within about ten minutes, the editor's **Settings → Email sender** says AWS is reviewing your request for production access
|
|
37
|
+
1. Within about ten minutes, the editor's **Settings → Email sender** says AWS is reviewing your request for production access, and later whether AWS has granted it.
|
|
38
|
+
|
|
39
|
+
Within seconds of every request, AWS sends an automated email asking for more information, in a support case. The SES console then shows **More information needed**, and the editor says AWS hasn't granted production access yet and has replied in AWS Support, with the case number. That's expected, and not a refusal: answer it as the next section describes.
|
|
38
40
|
|
|
39
41
|
## 3. Answer AWS's questions
|
|
40
42
|
|
|
41
|
-
AWS
|
|
43
|
+
AWS asks how you collect addresses and handle bounces before it decides, every time. A newsletter may be asked for more detail again, or turned down the first time, sometimes without a reason. A reply that explains how the list is built usually gets further than a new request. Reply in the support case, whichever it is, or open a new request from the **Account dashboard**. The editor reads the same until AWS grants access, since AWS's account reports a request for more information and a refusal alike.
|
|
42
44
|
|
|
43
45
|
In the sandbox, the subscribe form's confirmation email reaches only addresses verified in SES. If AWS's reviewer tries the form, say that this is why no confirmation arrives. In your reply, cover:
|
|
44
46
|
|
|
@@ -48,6 +50,7 @@ In the sandbox, the subscribe form's confirmation email reaches only addresses v
|
|
|
48
50
|
- That the publication's website shows what the newsletter is, and how readers subscribe.
|
|
49
51
|
- That an imported list was gathered with opt-in on the previous service.
|
|
50
52
|
- How many subscribers the newsletter expects, and how often it sends.
|
|
53
|
+
- A sample email. Send yourself a test of a post, as [Verify it works](07-verify.md#1-send-yourself-a-test-email) shows, and attach it to your reply or paste its text.
|
|
51
54
|
|
|
52
55
|
## 4. Check that AWS granted it
|
|
53
56
|
|
|
@@ -55,7 +58,7 @@ Until AWS grants production access, SES sends only to your verified addresses, s
|
|
|
55
58
|
|
|
56
59
|
1. In the editor, open **Settings → Email sender**. The email provider reads **Production**. From the API, `npx kestrel api GET /api/provider` shows `state` as `production`.
|
|
57
60
|
|
|
58
|
-
1. If it reads **Sandbox**, AWS hasn't granted access yet. Meanwhile, continue with [Verify it works](07-verify.md), which runs in the sandbox. Come back to this section when AWS answers
|
|
61
|
+
1. If it reads **Sandbox**, AWS hasn't granted access yet. If it also says AWS has replied, with a case number, answer in that case, as section 3 describes. Meanwhile, continue with [Verify it works](07-verify.md), which runs in the sandbox. Come back to this section when AWS answers.
|
|
59
62
|
|
|
60
63
|
1. If it says Kestrel's AWS key needs permission to read your account, select **Add the permission** and follow the step it opens. The app checks again within an hour.
|
|
61
64
|
|
|
@@ -41,9 +41,9 @@ If a check fails, compare your DNS records with the ones in [Connect Amazon SES]
|
|
|
41
41
|
|
|
42
42
|
## 3. Check that notifications arrive
|
|
43
43
|
|
|
44
|
-
Kestrel emails
|
|
44
|
+
Kestrel emails each notifications address when a send finishes, and right away when one runs into a problem. Check that they reach you, using one of the addresses you verified in SES. While SES is in the sandbox, every notifications address has to be verified there too, unless notifications go through [Cloudflare's email](../guides/03-notifications.md), where each is verified with Cloudflare instead. If you're setting Kestrel up for someone else, you can add their address beside yours later.
|
|
45
45
|
|
|
46
|
-
1. In the editor, open **Settings → Notifications**,
|
|
46
|
+
1. In the editor, open **Settings → Notifications**, type that address under **Send notifications to**, select **Add inbox**, and then **Save**.
|
|
47
47
|
|
|
48
48
|
1. Select **Send a test notification**. It arrives at that address.
|
|
49
49
|
|
|
@@ -8,10 +8,12 @@ The rest happens in the editor, at `https://newsletter.example.com/dashboard/`.
|
|
|
8
8
|
|
|
9
9
|
If someone else is the publisher, let them sign in first:
|
|
10
10
|
|
|
11
|
-
1. Add their email address to the policy on the Access application that lets you in, as in [
|
|
11
|
+
1. Add their email address to the policy on the Access application that lets you in, and to `ACCESS_ALLOWED_EMAILS` if you set it, as in [Give the app its two values](03-access.md#4-give-the-app-its-two-values).
|
|
12
12
|
|
|
13
13
|
1. If they don't have a Cloudflare account, add another way to sign in, such as a one-time code by email. It's under **Zero Trust → Integrations → Identity providers**.
|
|
14
14
|
|
|
15
|
+
1. Add their address under **Settings → Notifications** beside yours, so a problem reaches both of you.
|
|
16
|
+
|
|
15
17
|
1. Send them the editor's address and a link to [Name your publication](../publishing/03-name-your-publication.md).
|
|
16
18
|
|
|
17
19
|
## Optional next steps
|
|
@@ -43,7 +43,7 @@ Managed OAuth lets an app such as Claude act with a person's account, the way th
|
|
|
43
43
|
curl -si https://newsletter.example.com/api/whoami
|
|
44
44
|
```
|
|
45
45
|
|
|
46
|
-
|
|
46
|
+
You should see `401`, with a `www-authenticate` line that starts with `Bearer`. Access can take a few minutes more to start accepting sign-ins. Until it does, signing in shows `invalid_target`; wait, then try again.
|
|
47
47
|
|
|
48
48
|
## 2. Connect Claude, or send the publisher the address
|
|
49
49
|
|
|
@@ -2,13 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
Kestrel emails the publisher when a send finishes, and right away when one runs into a problem. By default, these notifications go through the email provider. The one that matters most, about the provider refusing your account, is the one the provider would refuse too. Cloudflare's email doesn't depend on the provider, so that notification still reaches the publisher.
|
|
4
4
|
|
|
5
|
-
In this guide, you turn on Cloudflare's email for your app's hostname, and verify the notifications
|
|
5
|
+
In this guide, you turn on Cloudflare's email for your app's hostname, and verify the notifications addresses with it. Then you add the binding that sends notifications through it, deploy, and send yourself a test.
|
|
6
6
|
|
|
7
7
|
## Before you begin
|
|
8
8
|
|
|
9
9
|
You need:
|
|
10
10
|
|
|
11
|
-
- The notifications
|
|
11
|
+
- The notifications addresses saved, from [Verify it works](../get-started/07-verify.md#3-check-that-notifications-arrive).
|
|
12
12
|
- Your domain's DNS on Cloudflare, as for the rest of the app.
|
|
13
13
|
|
|
14
14
|
## 1. Turn on Email Routing for your app's hostname
|
|
@@ -23,13 +23,13 @@ Cloudflare's email sends only from a domain set up with Email Routing. Use your
|
|
|
23
23
|
|
|
24
24
|
Avoid turning on Email Routing for a domain that already receives mail elsewhere, such as `example.com` with Google Workspace. Email Routing takes over that domain's MX records. Don't send notifications from `send.example.com` either, since your email provider sends from it. Email Routing there to forward readers' replies, as [Connect Amazon SES](../get-started/05-ses.md#8-receive-replies-at-your-from-address) sets up, is fine, since it only receives.
|
|
25
25
|
|
|
26
|
-
## 2. Verify the notifications
|
|
26
|
+
## 2. Verify the notifications addresses
|
|
27
27
|
|
|
28
|
-
Cloudflare's email delivers only to addresses you've verified with it. Sending to a verified address is free on any plan.
|
|
28
|
+
Cloudflare's email delivers only to addresses you've verified with it. Sending to a verified address is free on any plan. Notifications can go to several addresses, such as the publisher's and yours, and each one needs verifying.
|
|
29
29
|
|
|
30
|
-
1. Under **Email Routing**, go to **Destination Addresses**, and add
|
|
30
|
+
1. Under **Email Routing**, go to **Destination Addresses**, and add each address saved for notifications.
|
|
31
31
|
|
|
32
|
-
1. Open the email Cloudflare sends to
|
|
32
|
+
1. Open the email Cloudflare sends to each address, and select **Verify email address**. If it's the publisher's address, ask them to do this step.
|
|
33
33
|
|
|
34
34
|
A verified address belongs to your Cloudflare account, so every environment in it can use it.
|
|
35
35
|
|
|
@@ -41,11 +41,11 @@ A `send_email` binding named `NOTIFY` moves notifications to Cloudflare's email.
|
|
|
41
41
|
|
|
42
42
|
```jsonc
|
|
43
43
|
"send_email": [
|
|
44
|
-
{ "name": "NOTIFY", "allowed_destination_addresses": ["you@example.com"] } // ← the notifications
|
|
44
|
+
{ "name": "NOTIFY", "allowed_destination_addresses": ["you@example.com"] } // ← the notifications addresses
|
|
45
45
|
],
|
|
46
46
|
```
|
|
47
47
|
|
|
48
|
-
`allowed_destination_addresses` is optional. It limits the binding to the addresses you list, so a changed setting in the editor can't send notifications anywhere else. Leave it out to allow any address you've verified. If the publisher changes
|
|
48
|
+
`allowed_destination_addresses` is optional. It limits the binding to the addresses you list, so a changed setting in the editor can't send notifications anywhere else. Leave it out to allow any address you've verified. If the publisher adds or changes a notifications address later, verify the new one and add it here too.
|
|
49
49
|
|
|
50
50
|
1. **(Optional)** Notifications come from `Kestrel <kestrel@newsletter.example.com>`. To use another address on that hostname, set `NOTIFY_FROM` in the block's `vars`:
|
|
51
51
|
|
|
@@ -76,9 +76,9 @@ A `send_email` binding named `NOTIFY` moves notifications to Cloudflare's email.
|
|
|
76
76
|
|
|
77
77
|
1. In the editor, open **Settings → Notifications**. **Sent through** reads "Cloudflare Email, separate from your newsletter's provider", and **From address** shows `Kestrel <kestrel@newsletter.example.com>`.
|
|
78
78
|
|
|
79
|
-
1. Select **Send a test notification**. It arrives at
|
|
79
|
+
1. Select **Send a test notification**. It arrives at each notifications address, and **Last notification** shows it as delivered.
|
|
80
80
|
|
|
81
|
-
If the test isn't delivered, **Last notification** shows Cloudflare's reason, such as an address that isn't verified. Cloudflare's Email Routing summary lists notifications as dropped even when they're delivered, so check the notifications inbox rather than the summary.
|
|
81
|
+
If the test isn't delivered to an address, **Last notification** shows Cloudflare's reason, such as an address that isn't verified, after the address when there are several. The other addresses still get it. Cloudflare's Email Routing summary lists notifications as dropped even when they're delivered, so check the notifications inbox rather than the summary.
|
|
82
82
|
|
|
83
83
|
## What notifications don't cover
|
|
84
84
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
When something needs you, Kestrel says so in a line on the send's page, under **Sent**. The top of the dashboard shows it too. Red needs you to act, and amber is worth a look. The line says what happened, what to do, and who does it. This page covers what the screen can't: the fixes made outside Kestrel, and the decisions that are yours.
|
|
4
4
|
|
|
5
|
-
**Set a notifications address, so a problem reaches you without your watching.** Set it under **Settings → Notifications**, in **Send notifications to**. With none set, Kestrel emails you nothing. [Notifications](06-troubleshooting.md#notifications), below, lists what you're told.
|
|
5
|
+
**Set a notifications address, so a problem reaches you without your watching.** Set it under **Settings → Notifications**, in **Send notifications to**: type an address and choose **Add inbox**. Add the address of whoever runs your instance too, and both of you are told. With none set, Kestrel emails you nothing. [Notifications](06-troubleshooting.md#notifications), below, lists what you're told.
|
|
6
6
|
|
|
7
7
|
## A send is held or waiting
|
|
8
8
|
|
|
@@ -33,6 +33,27 @@ When none come, the send's page says so an hour after it went out. Your provider
|
|
|
33
33
|
|
|
34
34
|
While Amazon SES is in its sandbox, it mails only addresses verified in SES. Verify the address you test with in the SES console, or [request production access](../get-started/06-production-access.md). When the test's line says the provider didn't answer, check the inbox before you send it again, since the first may have arrived. A test SES refuses for your account has the same fix as [a send it refuses](06-troubleshooting.md#a-send-is-held-or-waiting).
|
|
35
35
|
|
|
36
|
+
## Signups aren't open yet, or are paused
|
|
37
|
+
|
|
38
|
+
Your subscribe page takes signups only while email is working, so a reader is never told a link is coming when none can go. Every visitor gets the same page, whatever address they enter.
|
|
39
|
+
|
|
40
|
+
- "Signups aren’t open yet" means SES hasn't sent an email for your publication yet. Send one from **Template → Send test email**, to an address verified in SES while it's in the sandbox. Once SES takes it, signups open. Before an email provider is connected, the page says the same, and [Connect Amazon SES](../get-started/05-ses.md) is the fix.
|
|
41
|
+
- "Signups are paused" means SES is refusing your email, every email including confirmations and tests. The dashboard's **Email isn’t going out** line gives SES's words and the fix, which whoever runs your instance makes, as for [a problem with Amazon SES](#a-problem-with-amazon-ses). Then send a test email: once SES takes one, signups reopen.
|
|
42
|
+
|
|
43
|
+
Kestrel notices a refusal when an email meets it, or within about ten minutes from its check of your SES account. A refusal the check found lifts on its own within about ten minutes of the fix; one an email met lifts when a test email goes through. A reader who signed up just as it started may have been told a link was coming, and can sign up again once signups reopen.
|
|
44
|
+
|
|
45
|
+
## A problem with Amazon SES
|
|
46
|
+
|
|
47
|
+
**Settings → Email sender** and the top of the dashboard name a problem with your Amazon SES account within about ten minutes, without sending anything. Red means no email can go out until it's fixed, tests and confirmations included. Amber is worth a look. Each line links the step that fixes it, and whoever runs your instance makes the fix:
|
|
48
|
+
|
|
49
|
+
- **The configuration set is missing, or its sending is off.** The set Kestrel sends with, the one `SES_CONFIGURATION_SET` names or the sending identity's default if that secret isn't set, isn't in your region, or its sending is turned off. Correct the name, or create the set, as [Connect Amazon SES](../get-started/05-ses.md#4-send-bounces-and-complaints-to-the-app) shows. Turn a set's sending back on in the SES console, under its configuration sets.
|
|
50
|
+
- **SES rejects the AWS key.** The key ID is unknown, the secret doesn't match it, or the AWS account isn't signed up for SES. Store the key's ID and secret again, or create a new key, as [Connect Amazon SES](../get-started/05-ses.md#5-create-the-apps-aws-key) shows.
|
|
51
|
+
- **The sender isn't verified.** SES has no verified identity for the From address in your region. Verify the sending hostname, as [Connect Amazon SES](../get-started/05-ses.md#1-verify-your-sending-hostname) shows.
|
|
52
|
+
- **Sending is turned off for the account.** AWS documents turning it back on only with the AWS CLI, naming your region: `aws sesv2 put-account-sending-attributes --sending-enabled --region us-east-1`.
|
|
53
|
+
- **AWS paused the account, or is reviewing it.** AWS does this when the account's bounce or complaint rate runs high. While it reviews, SES still sends, and the line is amber. Once it pauses sending, the line is red. Either way, AWS's email to the AWS account says what it needs: answer it in AWS Support, and check the addresses on your recent sends' records.
|
|
54
|
+
|
|
55
|
+
The app needs to read each of these. If its key isn't allowed to, the line says nothing rather than guessing. The policy in [Connect Amazon SES](../get-started/05-ses.md#5-create-the-apps-aws-key) allows all of it.
|
|
56
|
+
|
|
36
57
|
## Your list outgrows Workers Free
|
|
37
58
|
|
|
38
59
|
Upgrade the Cloudflare account to Workers Paid, and set `SUBREQUEST_BUDGET` so Kestrel sends faster, as [As your list grows](../guides/09-as-your-list-grows.md) describes. Kestrel warns you before a send that could use up the free plan's daily database writes, which stops the app until midnight UTC. [Bring your existing list](01-your-list.md#limits) explains the limits.
|
|
@@ -60,8 +81,10 @@ Nothing on your list changes. Kestrel refuses a file it can't read with certaint
|
|
|
60
81
|
Most failures are in your instance's Cloudflare Access setup, made by whoever runs your instance. [Connect Claude](08-connect-claude.md) covers connecting it. When that fails:
|
|
61
82
|
|
|
62
83
|
- **Claude says it couldn't connect, or the sign-in failed.** Your instance's Access application needs Managed OAuth set up as [Turn on Managed OAuth](../guides/01-allow-claude.md#1-turn-on-managed-oauth) describes, including Claude's sign-in address for Claude Desktop and claude.ai. Check that the connector's address is your publication's address followed by `/api/mcp`, and that your instance is deployed, not running on your own computer.
|
|
84
|
+
- Signing in shows `invalid_target` (`OAuth not enabled for the targeted resource`). Access can take a few minutes to start accepting sign-ins once Managed OAuth is on. Wait, then try again.
|
|
63
85
|
- `kestrel login` says your instance "doesn't offer a sign-in for programs". Managed OAuth is off. Turn it on, as the same section describes.
|
|
64
86
|
- `kestrel login` or `kestrel api` reports a `401`. Access signed you in, but Kestrel refused the sign-in. If you set `ACCESS_ALLOWED_EMAILS`, check that it includes the address you signed in with. If either names `access_unidentified`, it's the next problem. If `kestrel api` says your sign-in has ended, sign in again as [Connect Claude](08-connect-claude.md#claude-code) describes.
|
|
87
|
+
- `kestrel api` says it couldn't renew your sign-in because its Access team answered 404 (not found). First check which instance it called: when you don't name one, it prints a line naming it before "the only instance you're signed in to", which may not be the one you meant. Name yours with `--url`. If it is yours, the Access team domain was most likely renamed or removed since you signed in. Sign in again with `npx kestrel login` and your instance's address, which finds the team it uses now.
|
|
65
88
|
- **"Access let you in, but didn't say who you are".** Your login carried neither an email nor a service token. Some other kind of Access policy let it through, such as a certificate or device rule, or a login method that gives no email. Kestrel accepts only an Allow policy on people's emails and a Service Auth policy on a service token. Sign out, then sign in through the policy that lets you in by email. Whoever runs your instance removes the other policy from its Access application, or changes it to one of those two kinds. A script that gets this needs a service token under a Service Auth policy, as [Allow the token in](../guides/02-automate.md#2-allow-the-token-in) describes.
|
|
66
89
|
- **Claude's changes read "via the API".** Claude called the API some way other than `kestrel api`, which names Claude Code. Your instance's instructions for Claude Code have it use `kestrel api` once they're brought up to date, as [Install the release](../guides/07-upgrade.md#2-install-the-release) describes. Changes reading "a service token" mean Claude still uses one, and [Connect Claude](08-connect-claude.md#claude-code) signs it in as you instead.
|
|
67
90
|
|
|
@@ -76,9 +99,10 @@ With an address set, Kestrel emails you:
|
|
|
76
99
|
- when a send finishes, with how many emails were accepted, unsent, and skipped
|
|
77
100
|
- when your provider refuses your account or a send, or no provider is connected
|
|
78
101
|
- when the SES sandbox refuses a signup's confirmation, at most once a day
|
|
102
|
+
- when email isn't going out and signups are paused, found by a reader's signup or the check of your SES account, at most once a day while it lasts
|
|
79
103
|
- when a send stops going out with nothing to explain it, waits for you to resolve it, or misses its time
|
|
80
104
|
- when a service token imports a list, since no one saw it happen (an import you make yourself, or through Claude, sends nothing)
|
|
81
105
|
|
|
82
|
-
Each links to the page it's about, and is sent once while the problem lasts. A bounce spike shows on the dashboard but isn't emailed. **Send a test notification** checks that one arrives.
|
|
106
|
+
Each goes to every address set, links to the page it's about, and is sent once while the problem lasts. If one address can't be reached, the others still get it, and **Last notification** names the one that failed. A bounce spike shows on the dashboard but isn't emailed. **Send a test notification** checks that one arrives.
|
|
83
107
|
|
|
84
108
|
By default, notifications go through your newsletter's own email provider. So when that provider refuses your account, the notification about it can't reach you either. You can send them another way, as [Notifications through Cloudflare's email](../guides/03-notifications.md) describes.
|
|
@@ -14,7 +14,7 @@ How you connect depends on where you talk to Claude:
|
|
|
14
14
|
1. In Claude, go to **Customize → Connectors**, select **Add**, then **Add custom connector**.
|
|
15
15
|
1. Name it, and enter your publication's address followed by `/api/mcp`, such as `https://newsletter.example.com/api/mcp`.
|
|
16
16
|
1. Under **OAuth client**, choose **Register automatically**.
|
|
17
|
-
1. Select **Add**, then
|
|
17
|
+
1. Select **Add**, then **Connect**. Sign in the way you do for the dashboard if asked, and choose **Allow**.
|
|
18
18
|
|
|
19
19
|
On a Team or Enterprise plan, an Owner of your Claude organization adds the connector under **Organization settings → Connectors** instead, and you then select **Connect** on it. Anthropic's [custom connectors guide](https://support.claude.com/en/articles/11175166-get-started-with-custom-connectors-using-remote-mcp) has the details.
|
|
20
20
|
|
|
@@ -2,7 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
Your publication's subscribe page is at `/subscribe` on your publication's address, such as `https://newsletter.example.com/subscribe`. Copy it from the dashboard's checklist or **Settings → Ways to subscribe**, and share it wherever your readers are. A reader joins only once they confirm by email.
|
|
4
4
|
|
|
5
|
-
**Signups open only once an email provider is connected
|
|
5
|
+
**Signups open only once email is working.** Until an email provider is connected and has sent an email, such as a test from **Template → Send test email**, the page says the newsletter isn't taking subscribers yet. If your provider starts refusing your email, the page says signups are paused, and the dashboard says what to fix. Either way every visitor gets the same page and nothing is saved. You can share the link anyway, and it opens once email works ([Connect Amazon SES](../get-started/05-ses.md)).
|
|
6
|
+
|
|
7
|
+
**On Amazon SES, share it once AWS grants production access.** Until then, SES mails only addresses verified in it, so a reader who signs up never gets their confirmation email. The dashboard's checklist and an empty **Subscribers** list say so, and link [Request production access](../get-started/06-production-access.md). Importing your list is safe meanwhile, since an import sends no email.
|
|
6
8
|
|
|
7
9
|
## How a reader joins
|
|
8
10
|
|
|
@@ -1,25 +1,29 @@
|
|
|
1
1
|
# Check your setup
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Three checks before your readers get anything: is email getting out, will you hear about problems, and what will your readers get. The dashboard's setup steps tick each one off from what Kestrel sees, so you, and Claude, can tell what's left.
|
|
4
4
|
|
|
5
|
-
**Email has to be connected first.** Until it is, nothing can be sent, and the dashboard's
|
|
5
|
+
**Email has to be connected first.** Until it is, nothing can be sent, and **Send a test email** waits on the dashboard's **Connect Amazon SES** step, which [Connect Amazon SES](../get-started/05-ses.md) covers.
|
|
6
6
|
|
|
7
|
-
**Until AWS grants production access, Amazon SES mails only addresses verified in SES.** So test with a verified address, and hold the subscribe link until then. The
|
|
7
|
+
**Until AWS grants production access, Amazon SES mails only addresses verified in SES.** So test with a verified address, and hold the subscribe link until then. The dashboard's **Request production access** step says where AWS's review stands, and [Request production access](../get-started/06-production-access.md) covers the request.
|
|
8
8
|
|
|
9
|
-
## 1. Send
|
|
9
|
+
## 1. Send a test email
|
|
10
10
|
|
|
11
|
-
Choose **Template** in the sidebar, then **Send test email**, and send a sample post to your own inboxes. When it arrives, check:
|
|
11
|
+
Choose **Template** in the sidebar, then **Send test email**, and send a sample post to your own inboxes. This is the test that proves email gets out: signups stay closed until SES takes an email, and the step is checked off once it does. When it arrives, check:
|
|
12
12
|
|
|
13
13
|
- **The From name and address,** which readers see first. They're set when your instance is deployed.
|
|
14
14
|
- **Where it lands:** the inbox, Promotions, or spam. If it lands in spam, check your sending domain's records, as [Sending-domain DNS](../reference/02-sending-domain-dns.md) describes.
|
|
15
15
|
- **A reply.** Reply to the test, and check that it reaches your inbox. Kestrel never sees replies, so this depends on [forwarding your From address](../get-started/05-ses.md#8-receive-replies-at-your-from-address).
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
If SES refuses the test, the dashboard says why in red above the steps, with what to check. Send another test once it's fixed.
|
|
18
18
|
|
|
19
|
-
|
|
19
|
+
## 2. Get notified
|
|
20
|
+
|
|
21
|
+
Under **Settings → Notifications**, add your address under **Send notifications to** with **Add inbox**, save, and choose **Send a test notification**. It goes to each address. If someone else runs your instance, add their address too, so a problem reaches both of you. Kestrel then emails you when a send finishes or needs you, as [Notifications](06-troubleshooting.md#notifications) lists. The step is checked off once an address is saved and the last notification arrived. If the last notification didn't arrive, the dashboard says so above the steps, with the channel's words.
|
|
20
22
|
|
|
21
|
-
## 3.
|
|
23
|
+
## 3. See what your readers get
|
|
24
|
+
|
|
25
|
+
Open your subscribe page from **Settings → Ways to subscribe**, subscribe with a personal address, and confirm. You see what a new reader sees, and you appear under **Subscribers** as **Confirmed**.
|
|
22
26
|
|
|
23
|
-
|
|
27
|
+
Then, once your first post is scheduled, **Send a test email for your first post** from its page. Scheduling makes the exact email your list will get, so this test is what goes out, and the step is checked off once it's sent. A test sent from a draft is a preview and doesn't count, and a change to your template or publication identity re-makes the scheduled email, so the step opens again until you test the new one. [Write and send a post](04-write-a-post.md) covers scheduling.
|
|
24
28
|
|
|
25
29
|
**If you're bringing more than about 1,000 subscribers, upgrade to Cloudflare's Workers Paid plan before you import.** [Bring your existing list](01-your-list.md#limits) explains why.
|
|
@@ -4,12 +4,17 @@ These pages get you publishing in the dashboard, and cover the decisions to make
|
|
|
4
4
|
|
|
5
5
|
## Set up your publication
|
|
6
6
|
|
|
7
|
-
These are the steps
|
|
7
|
+
These are the dashboard's setup steps, in its order. Each is checked off from what Kestrel can see, which Claude reads too, except sharing your link, which Kestrel can't see. Connecting Amazon SES and requesting production access are for whoever runs your Kestrel instance, in [Connect Amazon SES](../get-started/05-ses.md) and [Request production access](../get-started/06-production-access.md). If you're starting without a list, skip importing one.
|
|
8
8
|
|
|
9
|
+
1. Connect Amazon SES
|
|
10
|
+
1. [Send a test email](10-check-your-setup.md)
|
|
9
11
|
1. [Name your publication](03-name-your-publication.md)
|
|
10
|
-
1.
|
|
12
|
+
1. Get notified
|
|
13
|
+
1. Request production access
|
|
11
14
|
1. [Bring your existing list](01-your-list.md)
|
|
12
|
-
1. [Write
|
|
15
|
+
1. [Write your first post](04-write-a-post.md)
|
|
16
|
+
1. Schedule your first post
|
|
17
|
+
1. Send a test email for your first post
|
|
13
18
|
1. [Share your subscribe link](09-share-your-subscribe-link.md)
|
|
14
19
|
|
|
15
20
|
## Go further
|
|
@@ -68,7 +68,7 @@ Vars go in the environment block's `vars`. Every value is a string.
|
|
|
68
68
|
|
|
69
69
|
- `FROM_ADDRESS` required with a real provider
|
|
70
70
|
|
|
71
|
-
The `From:` of every email, and the identity your provider authenticates. Refused unless it's an email address, optionally with a display name in front, like `Name <address>`, and never on `example.com`, unless `PROVIDER` is `fake`.
|
|
71
|
+
The `From:` of every email, and the identity your provider authenticates. Refused unless it's an email address, optionally with a display name in front, like `Name <address>`, and never on `example.com`, unless `PROVIDER` is `fake`. With Amazon SES, when no identity covering it is verified for sending in `AWS_REGION`, **Settings → Email sender** says so in red within about ten minutes, without sending anything.
|
|
72
72
|
|
|
73
73
|
It isn't the publication's name, tagline, or logo. You set those in the editor, under **Settings → Publication identity**. Until you do, the From address's display name stands in for the name, and with neither, the emails and reader pages show no name at all.
|
|
74
74
|
|
|
@@ -138,11 +138,11 @@ Store each with `npx wrangler secret put NAME --env production`, which prompts f
|
|
|
138
138
|
|
|
139
139
|
- `ACCESS_ALLOWED_EMAILS` optional
|
|
140
140
|
|
|
141
|
-
A comma-separated list of the people allowed to sign in. Without it, the app lets in anyone the Access policy allows, so it keeps the editor limited to named people if the policy is widened later. It applies to people only. A service token, the credential a script uses, carries no email, so the Access policy alone decides whether it gets in.
|
|
141
|
+
A comma-separated list of the people allowed to sign in, such as `you@example.com, publisher@example.com`. Spaces and capital letters don't matter. Without it, the app lets in anyone the Access policy allows, so it keeps the editor limited to named people if the policy is widened later. It applies to people only. A service token, the credential a script uses, carries no email, so the Access policy alone decides whether it gets in.
|
|
142
142
|
|
|
143
143
|
- `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY` required with SES
|
|
144
144
|
|
|
145
|
-
The IAM user's access key, which sends through SES.
|
|
145
|
+
The IAM user's access key, which sends through SES. When SES rejects it, because the key ID is unknown, the secret doesn't match it, or the AWS account isn't signed up for SES, **Settings → Email sender** says so in red within about ten minutes, without sending anything.
|
|
146
146
|
|
|
147
147
|
- `SNS_TOPIC_ARN` required with SES
|
|
148
148
|
|
|
@@ -150,7 +150,7 @@ Store each with `npx wrangler secret put NAME --env production`, which prompts f
|
|
|
150
150
|
|
|
151
151
|
- `SES_CONFIGURATION_SET` optional, recommended with SES
|
|
152
152
|
|
|
153
|
-
The configuration set named on every email, so SES reports bounces and complaints. A name SES can't find in the region holds every list send until it's fixed.
|
|
153
|
+
The configuration set named on every email, so SES reports bounces and complaints. A name SES can't find in the region holds every list send until it's fixed. **Settings → Email sender** says so in red within about ten minutes, without sending anything, and does the same when the set's sending is turned off, as long as the app's key may call `ses:GetConfigurationSet`.
|
|
154
154
|
|
|
155
155
|
- `DEV_AUTH_SECRET` never on a deployed environment
|
|
156
156
|
|
|
@@ -196,7 +196,7 @@ Wrangler doesn't carry bindings or vars over from the top level of `wrangler.jso
|
|
|
196
196
|
|
|
197
197
|
## What the app refuses
|
|
198
198
|
|
|
199
|
-
The app checks its configuration on every request. It refuses to run on one that would do the wrong thing quietly, and each setting above says what it refuses. In local development, `SIMULATE_SENDS` must also be off (`off`, `0`, `false`, `no`, or empty), `not-connected` to run as a deployment with no email provider yet, or the provider a local send models: `ses` or `generic` (also `1`, `on`, `true`, or `yes`), optionally followed by `:none` for no injected failures or `:realistic`, the default
|
|
199
|
+
The app checks its configuration on every request. It refuses to run on one that would do the wrong thing quietly, and each setting above says what it refuses. In local development, `SIMULATE_SENDS` must also be off (`off`, `0`, `false`, `no`, or empty), `not-connected` to run as a deployment with no email provider yet, or the provider a local send models: `ses` or `generic` (also `1`, `on`, `true`, or `yes`), optionally followed by `:none` for no injected failures or `:realistic`, the default, and, for `ses`, by `:refuse=` and a cause SES refuses every email for (`credentials`, `opt_in`, `configuration`, `sender`, `mail_from`, `paused`, `set_paused`, `suspended`, or `unsupported`), as in `ses:refuse=configuration`. `SIMULATE_ACCOUNT` must be off, or, beside `SIMULATE_SENDS=ses`, the SES account state local development stands in for: `sandbox`, `pending`, `denied`, `production`, or `permission`, optionally followed by `:unrouted` or by one account problem, which shows its warning: `:set_missing`, `:set_paused`, `:key_rejected`, `:sending_disabled`, `:shutdown`, `:sender_unverified`, or `:probation`, as in `production:set_missing`. Case doesn't matter. A deployed instance ignores both.
|
|
200
200
|
|
|
201
201
|
Until it's fixed, no sends go out, and each place says what its reader can act on:
|
|
202
202
|
|