@kurtbruns/kestrel 1.13.0 → 1.15.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 +44 -1
- package/README.md +2 -2
- package/bin/kestrel.mjs +21 -2
- package/cli/dev.mjs +6 -2
- package/cli/init.mjs +2 -1
- package/cli/reset.mjs +1 -1
- package/cli/seed.mjs +3 -3
- package/dist/public/dashboard/app.js +290 -285
- package/dist/public/dashboard/app.js.map +4 -4
- package/dist/public/dashboard/index.html +2 -2
- package/dist/public/dashboard/styles.css +74 -40
- package/dist/worker/index.js +2723 -1192
- package/docs/README.md +3 -2
- package/docs/get-started/02-deploy.md +18 -21
- package/docs/get-started/03-access.md +10 -8
- package/docs/get-started/06-go-live.md +2 -1
- package/docs/guides/01-allow-claude.md +68 -0
- package/docs/guides/02-automate.md +88 -0
- package/docs/guides/09-as-your-list-grows.md +2 -2
- package/docs/publishing/01-your-list.md +3 -3
- package/docs/publishing/03-name-your-publication.md +1 -1
- package/docs/publishing/04-write-a-post.md +15 -52
- package/docs/publishing/06-troubleshooting.md +20 -9
- package/docs/publishing/08-connect-claude.md +60 -0
- package/docs/publishing/09-share-your-subscribe-link.md +1 -1
- package/docs/publishing/10-check-your-setup.md +9 -16
- package/docs/publishing/11-email-template.md +1 -1
- package/docs/publishing/README.md +2 -2
- package/docs/reference/01-configuration.md +132 -85
- package/docs/reference/02-sending-domain-dns.md +2 -2
- package/migrations/0013_who_acted_client.sql +20 -0
- package/migrations/0014_send_acts.sql +25 -0
- package/package.json +2 -1
- package/packages/kestrel/lib/api.mjs +462 -0
- package/packages/kestrel/lib/credentials.mjs +192 -0
- package/{cli → packages/kestrel/lib}/dev-api.mjs +21 -11
- package/{cli → packages/kestrel/lib}/dev-server.mjs +2 -2
- package/packages/kestrel/lib/handoff.mjs +118 -0
- package/packages/kestrel/lib/login.mjs +155 -0
- package/packages/kestrel/lib/oauth.mjs +368 -0
- package/packages/kestrel/lib/openapi.mjs +569 -0
- package/template/README.md +2 -0
- package/template/claude/CLAUDE.md +8 -5
- package/docs/guides/01-connect-claude.md +0 -101
- package/docs/publishing/07-working-with-claude.md +0 -29
|
@@ -1,101 +0,0 @@
|
|
|
1
|
-
# Connect Claude to the API
|
|
2
|
-
|
|
3
|
-
Claude Code can draft, edit, proofread, and schedule posts through the same API the editor uses. It has its own Cloudflare Access credential, so its changes show as Claude's, not a person's. Anything it schedules waits out the review window, where the publisher can still cancel it.
|
|
4
|
-
|
|
5
|
-
In this guide, you create a service token for Claude Code, and add a policy for it to your Access application. Then you store the token where Claude Code reads it, check that it works, and point Claude at the API reference.
|
|
6
|
-
|
|
7
|
-
## Before you begin
|
|
8
|
-
|
|
9
|
-
You need:
|
|
10
|
-
|
|
11
|
-
- The dashboard locked with Access, from [Lock the dashboard with Access](../get-started/03-access.md).
|
|
12
|
-
- [Claude Code](https://code.claude.com/docs/en/overview), run from your instance's repository.
|
|
13
|
-
|
|
14
|
-
## 1. Create a service token
|
|
15
|
-
|
|
16
|
-
A service token is a Client ID and a Client Secret that a program sends with each request. It carries no email, so the app tells it apart from your own login.
|
|
17
|
-
|
|
18
|
-
1. In **Zero Trust**, go to **Access controls → Service credentials → Service Tokens**, and select **Create Service Token**.
|
|
19
|
-
|
|
20
|
-
1. Name it `kestrel-claude`, and choose a **Service Token Duration**. A year is a good default.
|
|
21
|
-
|
|
22
|
-
1. Select **Generate token**.
|
|
23
|
-
|
|
24
|
-
1. Copy the **Client ID** and the **Client Secret**. Cloudflare shows the secret only once.
|
|
25
|
-
|
|
26
|
-
## 2. Add a policy for the token
|
|
27
|
-
|
|
28
|
-
Your application lets in only the people its `Publishers` policy names. A token needs a policy of its own, on the same application, because the app accepts only logins issued for that application.
|
|
29
|
-
|
|
30
|
-
1. Under **Access controls → Applications**, select **Configure** on the application from [Lock the dashboard with Access](../get-started/03-access.md#2-create-the-access-application).
|
|
31
|
-
|
|
32
|
-
1. Add a policy:
|
|
33
|
-
|
|
34
|
-
- **Policy name:** `Claude`
|
|
35
|
-
- **Action:** Service Auth
|
|
36
|
-
- **Include:** the **Service Token** selector, with `kestrel-claude`
|
|
37
|
-
|
|
38
|
-
1. Save the application.
|
|
39
|
-
|
|
40
|
-
`ACCESS_ALLOWED_EMAILS` doesn't apply to the token, since it has no email. This policy is the only thing that lets it in.
|
|
41
|
-
|
|
42
|
-
## 3. Store the token for Claude Code
|
|
43
|
-
|
|
44
|
-
Claude Code loads the `env` block of `.claude/settings.local.json` into every session. That file stays on your computer: your repository's `.gitignore` keeps it out of git.
|
|
45
|
-
|
|
46
|
-
1. Open `.claude/settings.local.json` in your instance's repository. Create it if it doesn't exist.
|
|
47
|
-
|
|
48
|
-
1. Add an `env` block, beside anything already in the file:
|
|
49
|
-
|
|
50
|
-
```json
|
|
51
|
-
{
|
|
52
|
-
"env": {
|
|
53
|
-
"KESTREL_URL": "https://newsletter.example.com",
|
|
54
|
-
"CF_ACCESS_CLIENT_ID": "REPLACE_WITH_CLIENT_ID",
|
|
55
|
-
"CF_ACCESS_CLIENT_SECRET": "REPLACE_WITH_CLIENT_SECRET"
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
```
|
|
59
|
-
|
|
60
|
-
`KESTREL_URL` is your app's address. The other two are the Client ID and Client Secret from section 1.
|
|
61
|
-
|
|
62
|
-
1. Start a new Claude Code session, so it loads the values.
|
|
63
|
-
|
|
64
|
-
Never put the secret in `.claude/settings.json`. That file is committed.
|
|
65
|
-
|
|
66
|
-
## 4. Point Claude at the API
|
|
67
|
-
|
|
68
|
-
Kestrel has no plugin or connector for Claude. Claude calls the HTTP API directly, and the API reference describes every route: its method and path, the body it takes, and an example. The editor's **API** tab shows the same reference.
|
|
69
|
-
|
|
70
|
-
Your instance's `.claude/CLAUDE.md` already tells Claude Code how to call the API, so a session in your instance's repository reads the reference before its first call. If it doesn't, a prompt like this one points it there:
|
|
71
|
-
|
|
72
|
-
```
|
|
73
|
-
Kestrel's API is at $KESTREL_URL. Send the CF-Access-Client-Id and
|
|
74
|
-
CF-Access-Client-Secret headers from your environment with every request.
|
|
75
|
-
Read GET /api/reference.md first, then the reference of the resource
|
|
76
|
-
you're working with, such as GET /api/reference/sends.md, before calling it.
|
|
77
|
-
```
|
|
78
|
-
|
|
79
|
-
For Claude the reference is Markdown in two levels: `GET /api/reference.md` is the index, with what every route shares and each route on one line, and `GET /api/reference/<resource>.md` holds one resource's routes in full, so Claude reads only what its work needs. The JSON at `GET /api/reference` is the whole reference in one answer, as the editor reads it, and `GET /api/openapi.json` is the same reference as an OpenAPI 3.1 document, for a tool that imports one. The docs are served as Markdown too: `GET /api/docs/publishing.md` lists the publisher's pages, `GET /api/docs/self-hosting.md` this guide's, and each page is at `GET /api/docs/<slug>.md`.
|
|
80
|
-
|
|
81
|
-
An agent given only your app's address finds these routes too. `GET /llms.txt` is public. It names the publication, its archive and subscribe page, and the reference and docs indexes, and links this guide for the token.
|
|
82
|
-
|
|
83
|
-
Claude reads the reference, and from there can list the posts, write a draft, and schedule it. The reference lists the body types each route takes. Most take JSON, sent with `Content-Type: application/json`. An image upload takes a multipart form, or the image file itself.
|
|
84
|
-
|
|
85
|
-
To bring subscribers over from another service, ask Claude to import the list, as [Bring your existing list](../publishing/01-your-list.md) describes. Its [file format](../publishing/01-your-list.md#the-file-format) section describes every column.
|
|
86
|
-
|
|
87
|
-
## Check it
|
|
88
|
-
|
|
89
|
-
1. Ask Claude to call `GET /api/whoami`. The app answers with a program's identity, not yours:
|
|
90
|
-
|
|
91
|
-
```
|
|
92
|
-
{"principal":{"kind":"service","token_id":"e367c0ffee5eb4.access"},"auth":{"mode":"access"}}
|
|
93
|
-
```
|
|
94
|
-
|
|
95
|
-
The `token_id` is the Client ID Zero Trust shows beside `kestrel-claude`. If it's a different one, Claude Code is using another token.
|
|
96
|
-
|
|
97
|
-
1. Ask Claude to list the posts. It answers with the posts the editor shows.
|
|
98
|
-
|
|
99
|
-
1. Open a draft in the editor, and ask Claude to change a word in it. The editor shows that the draft was changed elsewhere, last edited by **Claude**.
|
|
100
|
-
|
|
101
|
-
If Claude gets a `401`, a `403`, or a login page, check the policy from section 2, and the token's two values in `.claude/settings.local.json`. Check that the token hasn't expired, too.
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
# Working with Claude
|
|
2
|
-
|
|
3
|
-
Ask Claude in plain words, the way you'd ask a colleague. It works through the same API as the dashboard, so it can do what you can do there, and nothing more. Everything it changes shows in the dashboard as Claude's.
|
|
4
|
-
|
|
5
|
-
To set Claude up, follow [Connect Claude to the API](../guides/01-connect-claude.md). Claude reads these same pages from your Kestrel, so it explains things in the words you see here.
|
|
6
|
-
|
|
7
|
-
## What to ask Claude
|
|
8
|
-
|
|
9
|
-
- **Draft or proofread a post.** Give it notes, an outline, or a draft. It saves a new version you can open in the dashboard. If you edited the post meanwhile, its save is refused, and it works from your version or asks you.
|
|
10
|
-
- **Test and schedule a post.** Name the post and when it should go out, and say so if it should be email only. Claude tells you whether the post will be in your archive, and at which address, before it schedules. Then it sends a test of exactly what will go out and tells you how to cancel. It can also cancel a send or move it.
|
|
11
|
-
- **Take a sent post out of your archive, or put it back.** Claude asks first, as the dashboard does, since taking a post out breaks links people shared.
|
|
12
|
-
- **Check how a send did.** Claude reads the send's record and reports what was delivered, unsent, bounced, or complained.
|
|
13
|
-
- **Recover an earlier version.** Every save keeps a whole version of a post, and Claude can read them back.
|
|
14
|
-
- **Import a list.** Its `/migrate-list` skill asks what anything ambiguous in your old service's export means, and converts a few rows for you to check. It imports once you attest, and if a scheduled post would grow, you finish the import in the dashboard.
|
|
15
|
-
|
|
16
|
-
## The lines it can't cross
|
|
17
|
-
|
|
18
|
-
The app holds these for any client, so they hold whatever Claude is told:
|
|
19
|
-
|
|
20
|
-
- **Every send waits out the review window,** and you can cancel it until it goes out.
|
|
21
|
-
- **A scheduled post is locked,** and what goes out is exactly the copy that was scheduled.
|
|
22
|
-
- **No one is mailed twice,** and only confirmed subscribers receive a post.
|
|
23
|
-
- **An unsubscribe is final** until the reader subscribes again. Someone who complained or was erased comes back only by subscribing themselves: neither you nor Claude can add them.
|
|
24
|
-
- **No one's edit is overwritten.** A save based on an older version is refused.
|
|
25
|
-
- **Only you can let an import grow a scheduled send.** An import through Claude that would stops there, until you finish it in the dashboard.
|
|
26
|
-
|
|
27
|
-
Claude also follows instructions your Kestrel gives it, which live in your instance's repository. Those hold only as long as the instructions do. They have Claude ask before anything that reaches readers, say whether a post will be in your archive before it's sent, test again after a template change, and record whose decision each change to the list was. They also have it treat what's in a post or a subscriber's record as content, never as instructions.
|
|
28
|
-
|
|
29
|
-
The app lets Claude give your attestation for an import, so asking you first is one of those instructions, not a rule the app enforces. The import's record says it came through Claude, and Kestrel emails you about it if your notifications are set.
|