@kurtbruns/kestrel 1.10.0 → 1.11.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/CHANGELOG.md +13 -1
- package/README.md +19 -29
- package/dist/public/dashboard/app.js +1 -1
- package/dist/public/dashboard/app.js.map +2 -2
- package/dist/public/dashboard/index.html +1 -1
- package/dist/worker/index.js +86 -190
- package/docs/README.md +2 -2
- package/docs/get-started/02-deploy.md +12 -5
- package/docs/get-started/03-access.md +2 -0
- package/docs/get-started/04-ses.md +14 -14
- package/docs/get-started/05-verify.md +19 -7
- package/docs/get-started/06-go-live.md +13 -55
- package/docs/guides/01-connect-claude.md +4 -4
- package/docs/guides/03-notifications.md +10 -10
- package/docs/guides/09-as-your-list-grows.md +62 -0
- package/docs/publishing/01-your-list.md +118 -47
- package/docs/publishing/02-sending-rules.md +5 -5
- package/docs/publishing/03-name-your-publication.md +7 -93
- package/docs/publishing/04-write-a-post.md +30 -73
- package/docs/publishing/06-troubleshooting.md +74 -0
- package/docs/publishing/07-working-with-claude.md +18 -37
- package/docs/publishing/09-share-your-subscribe-link.md +11 -59
- package/docs/publishing/10-check-your-setup.md +12 -30
- package/docs/publishing/11-email-template.md +34 -0
- package/docs/publishing/README.md +12 -11
- package/docs/reference/01-configuration.md +6 -6
- package/docs/reference/02-sending-domain-dns.md +1 -1
- package/package.json +1 -1
- package/template/claude/skills/migrate-list/SKILL.md +2 -2
- package/docs/guides/08-try-locally.md +0 -108
- package/docs/publishing/05-scheduling-and-sending.md +0 -72
- package/docs/publishing/06-after-a-send.md +0 -109
- package/docs/publishing/08-import-format.md +0 -103
package/CHANGELOG.md
CHANGED
|
@@ -10,6 +10,17 @@ To move a running instance from one version to another, follow [Upgrade to a new
|
|
|
10
10
|
|
|
11
11
|
<!-- Add entries under Added / Changed / Fixed / Breaking. One operator-facing line each; see .claude/rules/changelog.md. Upgrade steps (a migration, a new setting) go once, in the Upgrading paragraph, never on an entry. -->
|
|
12
12
|
|
|
13
|
+
## [1.11.0] - 2026-10-15
|
|
14
|
+
|
|
15
|
+
**Upgrading from 1.10.0.** No database migrations. Run `npm run check-context` in your instance's repository: `/migrate-list` now reads the import format from "Bring your existing list".
|
|
16
|
+
|
|
17
|
+
### Changed
|
|
18
|
+
|
|
19
|
+
- The Publishing docs are reorganized around what a publisher has to do or decide. Their sections are **Set up your publication**, **Go further** (Edit your email template, Working with Claude), and **Rules and troubleshooting** (Sending rules, Troubleshooting). Write and send a post covers scheduling too, Troubleshooting replaces After a send with each problem a send or an import reports, in its own words, and the import format is the last section of Bring your existing list, which opens with when to upgrade to Workers Paid.
|
|
20
|
+
- In Self-hosting, **As your list grows** is its own guide (raising Amazon SES limits and upgrading to Workers Paid, with how long a send takes after each), Verify it works ends by checking SES production access, and Go live hands over to the publisher, including letting them sign in through Access. The pages call whoever writes the newsletter the publisher. Try Kestrel locally first moves into the README, now the front page for someone deciding whether to run Kestrel, and working on Kestrel's own source moves to CONTRIBUTING.md.
|
|
21
|
+
- The Markdown of each removed page (`import-format`, `scheduling-and-sending`, `after-a-send`, `try-locally`) still answers at its old address, with the page that now holds its content. The import screen's **Import format reference →** opens the file format section.
|
|
22
|
+
- The operator's Claude context: `/migrate-list` reads the import format from `GET /api/docs/your-list.md`, since the separate import format page is gone.
|
|
23
|
+
|
|
13
24
|
## [1.10.0] - 2026-10-14
|
|
14
25
|
|
|
15
26
|
**Upgrading from 1.9.0.** No database migrations. Run `npm run check-context` in your instance's repository: this release changes the operator's Claude context (a new `/migrate-list` skill, and rules for erasing, for choosing between unsubscribing and blocking, and for reading the docs as Markdown), and a copy you've edited is merged by hand. Three API changes can break a script of yours, listed under Breaking. One that adds suppressions sends `reason: "manual"` or none, reads what was stored from the answer's `suppression`, and takes a 200 with `added: false` as already suppressed. One that clears suppressions takes a 409 `erasure_marker` on an erased address as final. One that reads `GET /api/docs` reads `sets` in place of `landing`.
|
|
@@ -422,7 +433,8 @@ The first tagged release: a self-contained newsletter app on a Cloudflare Worker
|
|
|
422
433
|
- A build-version stamp: every instance reports its version, commit, and build time, read-only in the editor and at `GET /api/version`.
|
|
423
434
|
- This changelog and a semantic-versioning release process.
|
|
424
435
|
|
|
425
|
-
[Unreleased]: https://github.com/kurtbruns/kestrel/compare/v1.
|
|
436
|
+
[Unreleased]: https://github.com/kurtbruns/kestrel/compare/v1.11.0...HEAD
|
|
437
|
+
[1.11.0]: https://github.com/kurtbruns/kestrel/releases/tag/v1.11.0
|
|
426
438
|
[1.10.0]: https://github.com/kurtbruns/kestrel/releases/tag/v1.10.0
|
|
427
439
|
[1.9.0]: https://github.com/kurtbruns/kestrel/releases/tag/v1.9.0
|
|
428
440
|
[1.8.0]: https://github.com/kurtbruns/kestrel/releases/tag/v1.8.0
|
package/README.md
CHANGED
|
@@ -1,59 +1,49 @@
|
|
|
1
1
|
# Kestrel
|
|
2
2
|
|
|
3
|
-
Kestrel is
|
|
3
|
+
Kestrel is an open-source newsletter app you run yourself, on Cloudflare and Amazon SES. You own the list, the proof each reader opted in, the record of every send, and a permanent archive of every post.
|
|
4
|
+
|
|
5
|
+
You write in Markdown, preview the exact email, and schedule it with a window to cancel. Bring your list from another service, and take it out whenever you like. Work in the editor, or ask Claude to do it through the same API.
|
|
4
6
|
|
|
5
7
|
See [getkestrel.dev](https://getkestrel.dev) to learn more about Kestrel.
|
|
6
8
|
|
|
7
|
-
##
|
|
9
|
+
## Try it locally
|
|
10
|
+
|
|
11
|
+
You can run Kestrel on your own computer to look around, with [Node.js](https://nodejs.org/) 22 or later. It needs no Cloudflare account, and email goes to a stand-in, so nothing reaches a real inbox.
|
|
8
12
|
|
|
9
|
-
|
|
13
|
+
In an empty folder, write a new instance and install it:
|
|
10
14
|
|
|
11
15
|
```bash
|
|
12
16
|
npx @kurtbruns/kestrel init
|
|
17
|
+
npm install
|
|
13
18
|
```
|
|
14
19
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
The docs are two sets, one per job. [Self-hosting](docs/README.md) is for whoever deploys and runs Kestrel. [Publishing](docs/publishing/README.md) is for whoever writes and sends the newsletter: setting up the publication, the list, writing, sending, and the rules a newsletter keeps. Both are in the editor's **Docs** tab, which opens on Publishing, and an instance serves every page as Markdown for Claude to read.
|
|
18
|
-
|
|
19
|
-
## Work on Kestrel
|
|
20
|
-
|
|
21
|
-
This repository is Kestrel's source. To run it on your computer, with [Node.js](https://nodejs.org/) 22 or later:
|
|
20
|
+
Copy the local settings, and start the app:
|
|
22
21
|
|
|
23
22
|
```bash
|
|
24
|
-
npm install
|
|
25
23
|
cp .dev.vars.example .dev.vars
|
|
26
24
|
npm run dev
|
|
27
25
|
```
|
|
28
26
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
### Demo data
|
|
27
|
+
The first time, it asks you to confirm the local database's migrations. Then open http://localhost:8787/dashboard/, with nothing to sign in with. If port 8787 is taken, the `Ready on` line shows the one it picked.
|
|
32
28
|
|
|
33
|
-
|
|
29
|
+
In a second terminal, load a demo publication with posts, subscribers, and past sends, or empty it again:
|
|
34
30
|
|
|
35
31
|
```bash
|
|
36
|
-
npm run seed
|
|
37
|
-
npm run
|
|
38
|
-
npm run reset # back to an empty instance
|
|
32
|
+
npm run seed
|
|
33
|
+
npm run reset
|
|
39
34
|
```
|
|
40
35
|
|
|
41
|
-
|
|
36
|
+
When you're ready to deploy, this folder can be your instance's repository. Push it to an empty GitHub repository, then follow [Deploy the app](docs/get-started/02-deploy.md#2-sign-in-to-cloudflare) from signing in to Cloudflare.
|
|
42
37
|
|
|
43
|
-
|
|
38
|
+
## Run your own
|
|
44
39
|
|
|
45
|
-
|
|
46
|
-
TOKEN=$(curl -s http://localhost:8787/api/dev/token?kind=service | jq -r .token)
|
|
47
|
-
curl -H "Authorization: Bearer $TOKEN" http://localhost:8787/api/posts
|
|
48
|
-
```
|
|
40
|
+
Kestrel installs from npm as [`@kurtbruns/kestrel`](https://www.npmjs.com/package/@kurtbruns/kestrel). Your instance lives in a small repository of your own, which holds its settings and names the release it runs. [Get started](docs/README.md#get-started) takes you from an empty repository to a live newsletter, and [Upgrade to a new release](docs/guides/07-upgrade.md) moves an instance to a newer one.
|
|
49
41
|
|
|
50
|
-
The editor's **
|
|
42
|
+
The docs are two sets, one per job. [Self-hosting](docs/README.md) is for whoever deploys and runs Kestrel. [Publishing](docs/publishing/README.md) is for whoever writes and sends the newsletter. Both are in the editor's **Docs** tab, and an instance serves every page as Markdown for Claude to read.
|
|
51
43
|
|
|
52
|
-
##
|
|
44
|
+
## Contributing
|
|
53
45
|
|
|
54
|
-
|
|
55
|
-
- **Working on the code:** [`.claude/CLAUDE.md`](.claude/CLAUDE.md) has the commands, conventions, and module boundaries.
|
|
56
|
-
- **Releases:** [`CHANGELOG.md`](CHANGELOG.md).
|
|
46
|
+
To work on Kestrel's own source, see [CONTRIBUTING.md](CONTRIBUTING.md). Releases are recorded in [CHANGELOG.md](CHANGELOG.md).
|
|
57
47
|
|
|
58
48
|
## License
|
|
59
49
|
|
|
@@ -798,7 +798,7 @@ linus@example.com\r
|
|
|
798
798
|
<div class="table-wrap"><table class="imp-sample"><thead><tr>${yo.map(k=>o`<th><code>${k}</code></th>`)}</tr></thead>
|
|
799
799
|
<tbody>${f.map(k=>o`<tr>${yo.map(T=>o`<td>${k[T]??""}</td>`)}</tr>`)}</tbody></table></div>
|
|
800
800
|
<p class="field-hint">Optional columns: ${_.map((k,T)=>o`${T?", ":""}<code>${k}</code>`)}.</p>
|
|
801
|
-
<div class="row imp-links"><button type="button" class="ghost" data-template="csv">Download example file (CSV)</button><a class="ghost-link" href="#/docs/
|
|
801
|
+
<div class="row imp-links"><button type="button" class="ghost" data-template="csv">Download example file (CSV)</button><a class="ghost-link" href="#/docs/your-list/the-file-format">Import format reference →</a></div>`}function c(){return o`<p class="imp-text">A <code>.txt</code> file with one address per line.</p>
|
|
802
802
|
${_t(bo,o`${ho.replace(/\r\n/g,`
|
|
803
803
|
`).trim()}`,ho)}
|
|
804
804
|
<p class="imp-text">Next you’ll say what they are: subscribed (the default), unsubscribed, or bounced.</p>
|