@rubytech/create-maxy-code 0.1.282 → 0.1.283
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/package.json +1 -1
- package/payload/platform/config/brand.json +0 -1
- package/payload/platform/plugins/admin/skills/platform-architecture/SKILL.md +4 -4
- package/payload/platform/plugins/cloudflare/.claude-plugin/plugin.json +1 -1
- package/payload/platform/plugins/cloudflare/PLUGIN.md +5 -5
- package/payload/platform/plugins/cloudflare/references/api.md +76 -37
- package/payload/platform/plugins/cloudflare/references/d1-data-capture.md +27 -21
- package/payload/platform/plugins/cloudflare/references/dashboard-guide.md +3 -2
- package/payload/platform/plugins/cloudflare/references/hosting-sites.md +7 -5
- package/payload/platform/plugins/cloudflare/references/manual-setup.md +3 -2
- package/payload/platform/plugins/cloudflare/skills/cloudflare/SKILL.md +6 -6
- package/payload/platform/plugins/docs/references/telegram-guide.md +3 -3
- package/payload/platform/plugins/telegram/PLUGIN.md +7 -2
- package/payload/platform/plugins/telegram/mcp/dist/index.js +66 -11
- package/payload/platform/plugins/telegram/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/telegram/mcp/dist/lib/account-token.d.ts +6 -0
- package/payload/platform/plugins/telegram/mcp/dist/lib/account-token.d.ts.map +1 -0
- package/payload/platform/plugins/telegram/mcp/dist/lib/account-token.js +20 -0
- package/payload/platform/plugins/telegram/mcp/dist/lib/account-token.js.map +1 -0
- package/payload/platform/plugins/telegram/mcp/dist/lib/account-write.d.ts +21 -0
- package/payload/platform/plugins/telegram/mcp/dist/lib/account-write.d.ts.map +1 -0
- package/payload/platform/plugins/telegram/mcp/dist/lib/account-write.js +52 -0
- package/payload/platform/plugins/telegram/mcp/dist/lib/account-write.js.map +1 -0
- package/payload/platform/plugins/telegram/mcp/dist/tools/message.d.ts.map +1 -1
- package/payload/platform/plugins/telegram/mcp/dist/tools/message.js +5 -2
- package/payload/platform/plugins/telegram/mcp/dist/tools/message.js.map +1 -1
- package/payload/platform/plugins/telegram/references/setup-guide.md +10 -7
- package/payload/platform/plugins/telegram/skills/configure/SKILL.md +79 -0
- package/payload/platform/services/claude-session-manager/dist/http-server.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/http-server.js +17 -0
- package/payload/platform/services/claude-session-manager/dist/http-server.js.map +1 -1
- package/payload/server/public/assets/AdminShell-8Y7-maNi.js +1 -0
- package/payload/server/public/assets/{admin-0_VxffD5.js → admin-DyuZ4NT5.js} +1 -1
- package/payload/server/public/assets/{browser-Cxjkhtsk.js → browser-XrVAUQdG.js} +1 -1
- package/payload/server/public/assets/{data-BOgwOWnw.js → data-tcu3MXmK.js} +1 -1
- package/payload/server/public/assets/{graph-BqD2GjQd.js → graph-I4GyC2MW.js} +1 -1
- package/payload/server/public/browser.html +2 -2
- package/payload/server/public/data.html +2 -2
- package/payload/server/public/graph.html +2 -2
- package/payload/server/public/index.html +2 -2
- package/payload/server/server.js +827 -531
- package/payload/server/public/assets/AdminShell-FO766lOW.js +0 -1
package/package.json
CHANGED
|
@@ -55,7 +55,6 @@
|
|
|
55
55
|
},
|
|
56
56
|
|
|
57
57
|
"externalPlugins": [
|
|
58
|
-
{ "name": "telegram", "marketplace": "claude-plugins-official", "channelPlugin": true },
|
|
59
58
|
{ "name": "discord", "marketplace": "claude-plugins-official", "channelPlugin": true },
|
|
60
59
|
{ "name": "imessage", "marketplace": "claude-plugins-official", "channelPlugin": true },
|
|
61
60
|
{ "name": "superpowers", "marketplace": "claude-plugins-official" },
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: platform-architecture
|
|
3
3
|
description: Use when grounding any documented-surface claim about what Maxy ships — plugins, skills, specialists, install/deploy flows, internals. This is the install catalogue, not evidence of what is enabled on the current account. For install state on this account, call `capabilities-here`; for documented surface, cite the `Source:` URL inline.
|
|
4
|
-
content-hash: sha256:
|
|
4
|
+
content-hash: sha256:5dae80dcc0e3d36e755dcf26570c4d7c8893b6198b8cd769b3e5c6c1c79169f5
|
|
5
5
|
brand: maxy-code
|
|
6
6
|
product-name: Maxy
|
|
7
7
|
---
|
|
@@ -1728,13 +1728,13 @@ The Telegram plugin connects Maxy to a Telegram bot. Once set up, you can:
|
|
|
1728
1728
|
|
|
1729
1729
|
### Step 2: Connect the plugin
|
|
1730
1730
|
|
|
1731
|
-
Tell Maxy: "Set up Telegram" or "Configure the Telegram bot."
|
|
1731
|
+
Tell Maxy: "Set up Telegram" or "Configure the Telegram bot." Setup runs entirely in the admin chat — there is no settings page.
|
|
1732
1732
|
|
|
1733
|
-
Maxy
|
|
1733
|
+
Maxy asks for your bot token, then asks whether this is an **admin bot** (only your numeric Telegram IDs may message it) or a **public bot** (customer-facing, with a DM policy). It verifies the token, registers the bot's webhook on your brand's edge, stores the token under your brand's config (never in a shared system directory), and — for a public bot — sets the agent that will answer. The bot is now connected.
|
|
1734
1734
|
|
|
1735
1735
|
### Step 3: Start the bot
|
|
1736
1736
|
|
|
1737
|
-
In Telegram, open your bot and send `/start
|
|
1737
|
+
In Telegram, open your bot and send `/start`, then a message. Maxy receives it and replies in the same chat. Admin-bot messages from your registered IDs reach the admin agent; public-bot messages reach the public agent you selected.
|
|
1738
1738
|
|
|
1739
1739
|
## Sending Messages via Maxy
|
|
1740
1740
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cloudflare",
|
|
3
|
-
"description": "Cloudflare operations — tunnel setup/reset, DNS, Pages hosting, D1 data capture, and dashboard guidance. Zero agent-facing MCP tools; every operation is the agent invoking `cloudflared` or `wrangler` directly via Bash, calling the Cloudflare API with a
|
|
3
|
+
"description": "Cloudflare operations — tunnel setup/reset, DNS, Pages hosting, D1 data capture, and dashboard guidance. Zero agent-facing MCP tools; every operation is the agent invoking `cloudflared` or `wrangler` directly via Bash, calling the Cloudflare API with a reused per-scope narrow token, or quoting a dashboard click-path the operator performs themselves.",
|
|
4
4
|
"version": "0.1.0",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Rubytech LLC"
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: cloudflare
|
|
3
|
-
description: Cloudflare operations — tunnel setup/reset, DNS, Pages hosting, D1 data capture, and dashboard guidance. Zero agent-facing MCP tools; every operation is the agent invoking `cloudflared` or `wrangler` directly via Bash, calling the Cloudflare API with a
|
|
3
|
+
description: Cloudflare operations — tunnel setup/reset, DNS, Pages hosting, D1 data capture, and dashboard guidance. Zero agent-facing MCP tools; every operation is the agent invoking `cloudflared` or `wrangler` directly via Bash, calling the Cloudflare API with a reused per-scope narrow token, or quoting a dashboard click-path the operator performs themselves.
|
|
4
4
|
tools: []
|
|
5
5
|
mcp-manifest: skip
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
# Cloudflare Tunnel
|
|
9
9
|
|
|
10
|
-
Each installation has its own Cloudflare account. Two auth paths serve two operation classes. **Tunnel** auth is OAuth: the operator signs in via `cloudflared tunnel login` (issued by the agent through Bash); `cloudflared` writes `cert.pem` to the brand-scoped config directory. **API** auth (DNS, zones, Pages, D1, Access, token
|
|
10
|
+
Each installation has its own Cloudflare account. Two auth paths serve two operation classes. **Tunnel** auth is OAuth: the operator signs in via `cloudflared tunnel login` (issued by the agent through Bash); `cloudflared` writes `cert.pem` to the brand-scoped config directory. **API** auth (DNS, zones, Pages, D1, Access, token management) is the master token: the operator provisions one fully-scoped master token in the dashboard (an advanced, operator-guided step the agent never automates), stored at the account-scoped secrets file; the agent reads it only to provision **one stable narrow token per scope** — minting it once if absent, persisting it to the secrets file, reusing it thereafter — and periodically reconciles strays. Account state is brand-isolated and the master token never leaves the secrets file — see `references/api.md`.
|
|
11
11
|
|
|
12
12
|
## When to activate
|
|
13
13
|
|
|
@@ -25,7 +25,7 @@ Each installation has its own Cloudflare account. Two auth paths serve two opera
|
|
|
25
25
|
|
|
26
26
|
## Operator-facing surface
|
|
27
27
|
|
|
28
|
-
The plugin registers no agent-facing MCP tools. Every Cloudflare operation is the agent invoking `cloudflared` or `wrangler` directly via Bash, or calling the Cloudflare API with a
|
|
28
|
+
The plugin registers no agent-facing MCP tools. Every Cloudflare operation is the agent invoking `cloudflared` or `wrangler` directly via Bash, or calling the Cloudflare API with a reused per-scope narrow token, with stdout/stderr streamed verbatim into the PTY chat (secrets redacted). The OAuth URL printed by `cloudflared tunnel login` is linkified by the native PTY; the operator clicks it in their own browser. There is no shell-script wrapper, no orchestrator state machine, no MCP-tool surface.
|
|
29
29
|
|
|
30
30
|
### Skills
|
|
31
31
|
|
|
@@ -38,7 +38,7 @@ The plugin registers no agent-facing MCP tools. Every Cloudflare operation is th
|
|
|
38
38
|
| Reference | Topics |
|
|
39
39
|
|---|---|
|
|
40
40
|
| [manual-setup.md](references/manual-setup.md) | Tunnel runbook — Steps 0–7 with isolated `cloudflared` command blocks. The agent reads the relevant step before issuing each command. |
|
|
41
|
-
| [api.md](references/api.md) | Cloudflare API library — canonical docs URL + curated endpoint map (DNS, zones, tunnels, token create/verify), the advanced master-token creation walkthrough, the account-scoped secrets-file storage convention,
|
|
41
|
+
| [api.md](references/api.md) | Cloudflare API library — canonical docs URL + curated endpoint map (DNS, zones, tunnels, token list/create/verify/revoke), the advanced master-token creation walkthrough, the account-scoped secrets-file storage convention, the agent's reuse-a-stable-per-scope-token discipline, and the standing reconcile pass that revokes strays. |
|
|
42
42
|
| [d1-data-capture.md](references/d1-data-capture.md) | Form → Pages Function → D1 store → read/sweep. The Pages-Edit **and** D1-Edit token-scope requirement, `wrangler d1 create`/`execute --remote`, the `swept` column. |
|
|
43
43
|
| [hosting-sites.md](references/hosting-sites.md) | Deploy a static or Next.js site to Cloudflare Pages via `wrangler pages deploy`. |
|
|
44
44
|
| [web-analytics.md](references/web-analytics.md) | Why a registered site reports 0 visitors — the RUM API is blocked (no permission group), registered ≠ collecting, edge auto-injection is impossible for a Pages-only custom domain, and the beacon must be injected into the site HTML, rebuilt, and redeployed. Outcome: beacon present in live HTML. |
|
|
@@ -59,4 +59,4 @@ The setup-done claim only fires when `curl -I https://<hostname>` issued from ou
|
|
|
59
59
|
|
|
60
60
|
## Discipline
|
|
61
61
|
|
|
62
|
-
The agent's permitted surfaces are: direct `cloudflared` and `wrangler` invocations via Bash following the references; Cloudflare API calls authenticated by a
|
|
62
|
+
The agent's permitted surfaces are: direct `cloudflared` and `wrangler` invocations via Bash following the references; Cloudflare API calls authenticated by a reused per-scope narrow token, minted once if absent and persisted to the secrets file (the master token stays in that same file, never on a command line that echoes, never in chat); the reference files; and live verification (`curl -I` for tunnels, the deployed URL for hosting, a `SELECT` for D1). Out of bounds: Playwright or Chrome DevTools driving the dashboard, browser-automating master-token creation, WebSearch-for-CF-recipes, ad-hoc `cloudflared` flag invention not in the runbook, and writing or echoing any token. When a step fails, the agent reports the exact output (secrets redacted), cites the recovery step from `references/reset-guide.md`, and stops.
|
|
@@ -1,19 +1,19 @@
|
|
|
1
|
-
# Cloudflare API — master token,
|
|
1
|
+
# Cloudflare API — master token, reused per-scope narrow tokens, endpoint map
|
|
2
2
|
|
|
3
|
-
The Cloudflare REST API is a permitted surface on this install. It is how the agent does DNS edits, Pages deploys, D1 queries, Access policies, and apex-CNAME creation without driving the dashboard. This reference is the library: where the canonical docs live, how auth works (one operator-provisioned master token; the agent mints
|
|
3
|
+
The Cloudflare REST API is a permitted surface on this install. It is how the agent does DNS edits, Pages deploys, D1 queries, Access policies, and apex-CNAME creation without driving the dashboard. This reference is the library: where the canonical docs live, how auth works (one operator-provisioned master token; the agent mints **one narrow token per scope**, persists it to the secrets file, and reuses it), where the master is stored, and the curated endpoints worth knowing — including how to list and revoke tokens, and the standing reconcile pass that keeps the account from accumulating strays.
|
|
4
4
|
|
|
5
5
|
Canonical reference (always the source of truth for request shapes, never mirror it wholesale here): **https://developers.cloudflare.com/api/**
|
|
6
6
|
|
|
7
7
|
---
|
|
8
8
|
|
|
9
|
-
## Auth model — one master,
|
|
9
|
+
## Auth model — one master, reused per-scope narrow tokens
|
|
10
10
|
|
|
11
11
|
There are two distinct tokens in play. Keep them separate.
|
|
12
12
|
|
|
13
|
-
- **Master token** — fully-scoped, long-lived, provisioned **once** by the operator in the dashboard (see § Provisioning the master token, below). Broad enough to manage Pages, D1, DNS, **and** create API tokens. The agent reads it only to mint
|
|
14
|
-
- **
|
|
13
|
+
- **Master token** — fully-scoped, long-lived, provisioned **once** by the operator in the dashboard (see § Provisioning the master token, below). Broad enough to manage Pages, D1, DNS, **and** create API tokens. The agent reads it only to mint per-scope tokens. It is never passed to `wrangler`, never put on a command line that gets echoed, never printed.
|
|
14
|
+
- **Per-scope narrow token** — scoped to exactly one operation class (a single-zone DNS edit, a one-project Pages deploy, a D1 query), with a **deterministic name** (`<brand>-pages-d1`, `<brand>-dns`, `<brand>-access`). There is **one** such token per scope. The agent loads it from the secrets file; **if absent, it mints it once** from the master — correctly scoped, **no expiry — permanent and reused** — and persists it back to the secrets file. Thereafter it is loaded and reused, never re-minted. Exported into the environment for the one `wrangler` / API call; never echoed into chat, never written into a project tree.
|
|
15
15
|
|
|
16
|
-
Why
|
|
16
|
+
Why one-per-scope instead of one-per-operation: the operator provisions the master once; the agent provisions each narrow token once and reuses it, so the account never fills with throwaway tokens, no broad token is ever exported to a tool that could log it, and decay is handled structurally by the reconcile pass (§ Reconcile) rather than by per-token TTL. The narrow token persists alongside the master in the same `600`-mode secrets file but is **strictly narrower** than the master — so the binding "never export the master to `wrangler`" is preserved while the narrow token gains reuse.
|
|
17
17
|
|
|
18
18
|
---
|
|
19
19
|
|
|
@@ -38,7 +38,7 @@ Rationale (binding):
|
|
|
38
38
|
|
|
39
39
|
1. **Account-isolated** — under `data/accounts/<accountId>/`, consistent with brand isolation; one account's master never sits where another account's flow can read it.
|
|
40
40
|
2. **Outside every deployable/git project tree** — so the god-token can never be committed or shipped in a Pages upload. This is the failure mode a project-root `.env` invites: a `.env` next to a site's source gets swept into the `wrangler pages deploy` upload or a `git add .`.
|
|
41
|
-
3. **Sourced on demand
|
|
41
|
+
3. **Sourced on demand** — `set -a; . <file>; set +a` to load `CLOUDFLARE_API_TOKEN` (master) + `CLOUDFLARE_ACCOUNT_ID` into the environment. The reused per-scope narrow tokens (`<brand>-pages-d1`, etc.) persist **in this same file** as additional keys (e.g. `CF_PAGES_D1_TOKEN=`), minted once if absent and loaded thereafter; each is strictly narrower than the master that already lives here, so persisting it adds no exposure the master did not already carry. A per-scope token is exported only into the single command that uses it, never echoed.
|
|
42
42
|
|
|
43
43
|
Create it once (mode 600, parent dirs 700):
|
|
44
44
|
|
|
@@ -116,49 +116,55 @@ Cross-type calls fail with a stable, recognizable signal: a `cfat_…` token at
|
|
|
116
116
|
|
|
117
117
|
---
|
|
118
118
|
|
|
119
|
-
##
|
|
119
|
+
## Provisioning and reusing a stable per-scope token
|
|
120
120
|
|
|
121
|
-
|
|
121
|
+
Per operation class there is **one** deterministically-named narrow token, reused across runs. On each run the agent **loads it from the secrets file; if it is absent, it mints it once** (correctly scoped, **no expiry**) and persists it back to the secrets file. There is no per-operation throwaway and no `unset` — the token is permanent and reused. Mint via `POST /accounts/{account_id}/tokens` (`cfat_…` master) or `POST /user/tokens` (`cfut_…` master — see § Token type — route endpoints by prefix).
|
|
122
122
|
|
|
123
123
|
```bash
|
|
124
|
-
set -a; . "${SECRETS_DIR}/cloudflare.env"; set +a # loads the master + account id
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
124
|
+
set -a; . "${SECRETS_DIR}/cloudflare.env"; set +a # loads the master + account id (+ any persisted per-scope tokens)
|
|
125
|
+
: "${CLOUDFLARE_API_TOKEN:?credentials not loaded}"; : "${CLOUDFLARE_ACCOUNT_ID:?account id not loaded}"
|
|
126
|
+
|
|
127
|
+
# Example: the stable Pages-deploy token. One deterministic name + secrets key per scope.
|
|
128
|
+
TOKEN_KEY="CF_PAGES_D1_TOKEN" # the persisted key in cloudflare.env
|
|
129
|
+
SCOPE_NAME="<brand>-pages-d1" # the deterministic, reused token name on the account
|
|
130
|
+
PAGES_D1=$(grep -m1 "^${TOKEN_KEY}=" "${SECRETS_DIR}/cloudflare.env" 2>/dev/null | cut -d= -f2-)
|
|
131
|
+
|
|
132
|
+
if [ -z "${PAGES_D1}" ]; then # absent → mint once, scoped, no expiry, then persist
|
|
133
|
+
PAGES_D1=$(curl -sS -X POST \
|
|
134
|
+
-H "Authorization: Bearer ${CLOUDFLARE_API_TOKEN}" \
|
|
135
|
+
-H "Content-Type: application/json" \
|
|
136
|
+
"https://api.cloudflare.com/client/v4/accounts/${CLOUDFLARE_ACCOUNT_ID}/tokens" \
|
|
137
|
+
--data @- <<JSON | jq -r '.result.value'
|
|
132
138
|
{
|
|
133
|
-
"name": "
|
|
139
|
+
"name": "${SCOPE_NAME}",
|
|
134
140
|
"policies": [{
|
|
135
141
|
"effect": "allow",
|
|
136
|
-
"resources": { "com.cloudflare.api.account
|
|
142
|
+
"resources": { "com.cloudflare.api.account.${CLOUDFLARE_ACCOUNT_ID}": "*" },
|
|
137
143
|
"permission_groups": [{ "id": "<pages-edit-permission-group-id>", "name": "Pages Write" }]
|
|
138
|
-
}]
|
|
139
|
-
"expires_on": "<iso8601-a-few-minutes-out>"
|
|
144
|
+
}]
|
|
140
145
|
}
|
|
141
146
|
JSON
|
|
142
|
-
)
|
|
147
|
+
)
|
|
148
|
+
( umask 077; printf '%s=%s\n' "${TOKEN_KEY}" "${PAGES_D1}" >> "${SECRETS_DIR}/cloudflare.env" )
|
|
149
|
+
fi
|
|
143
150
|
|
|
144
|
-
#
|
|
145
|
-
CLOUDFLARE_API_TOKEN="${
|
|
146
|
-
|
|
147
|
-
unset MINTED # discard; never written to disk, never echoed
|
|
151
|
+
# Reused thereafter — exported only into the one command's environment, never echoed:
|
|
152
|
+
CLOUDFLARE_API_TOKEN="${PAGES_D1}" wrangler pages deploy ./dist --project-name <project> --branch=main
|
|
148
153
|
```
|
|
149
154
|
|
|
150
|
-
Look up `<pages-edit-permission-group-id>` (and the ids for DNS Edit, D1 Edit, etc.) once via `GET /accounts/{account_id}/tokens/permission_groups`; they are stable per account.
|
|
155
|
+
No `expires_on`: the per-scope token is permanent and reused, so decay is the job of the **reconcile pass** (§ Reconcile), not a per-token TTL — the old short-TTL guidance was never honored in practice and produced only never-expiring strays. Look up `<pages-edit-permission-group-id>` (and the ids for DNS Edit, D1 Edit, etc.) once via `GET /accounts/{account_id}/tokens/permission_groups`; they are stable per account. Before reusing or before reconciling, `GET …/tokens` (§ Curated endpoint map) shows what already exists, so a scope is never minted twice under different names.
|
|
151
156
|
|
|
152
157
|
**Redaction is binding.** Every example above pipes the token into a variable or an `Authorization` header and never to stdout. The agent surfaces the operation as verb + target — "minting a Pages-deploy token", "creating CNAME `chat.example.com`" — never the secret. A failure surfaces the API error body with any `Authorization` value masked.
|
|
153
158
|
|
|
154
159
|
### A freshly-minted token is not instantly usable — verify with backoff
|
|
155
160
|
|
|
156
|
-
A just-minted token returns `10000 Authentication error` for a few seconds before it becomes active. Do not fire a fresh token's first real call blind; **verify it first**, retrying on a transient `10000` with backoff and giving up after ~30s. Route `${VERIFY_URL}` by the master's prefix (account- vs user-scoped — see § Token type):
|
|
161
|
+
This applies only on the **mint-once path** (the absent branch above) — a reused token loaded from secrets is already active and needs no wait. A just-minted token returns `10000 Authentication error` for a few seconds before it becomes active. Do not fire a fresh token's first real call blind; **verify it first**, retrying on a transient `10000` with backoff and giving up after ~30s. Route `${VERIFY_URL}` by the master's prefix (account- vs user-scoped — see § Token type):
|
|
157
162
|
|
|
158
163
|
```bash
|
|
159
|
-
#
|
|
164
|
+
# Run this inside the mint-once (absent) branch, before persisting/using the token.
|
|
165
|
+
# ${PAGES_D1} = the per-scope token just minted; ${VERIFY_URL} = the prefix-correct verify endpoint.
|
|
160
166
|
for i in $(seq 1 10); do
|
|
161
|
-
s=$(curl -sS -H "Authorization: Bearer ${
|
|
167
|
+
s=$(curl -sS -H "Authorization: Bearer ${PAGES_D1}" "${VERIFY_URL}" \
|
|
162
168
|
| jq -r '.result.status // (.errors[0].code | tostring)')
|
|
163
169
|
[ "$s" = "active" ] && break
|
|
164
170
|
[ "$s" = "10000" ] && { sleep 3; continue; } # transient post-mint window — expected
|
|
@@ -172,12 +178,14 @@ Proceed to the operation only once verify returns `active`. The `10000` window i
|
|
|
172
178
|
|
|
173
179
|
## Curated endpoint map
|
|
174
180
|
|
|
175
|
-
Request/response shapes live at the canonical docs URL; this is the index of what to reach for. `${ACC}` = `CLOUDFLARE_ACCOUNT_ID`, `${ZONE}` = the zone id, `${TOKEN}` = a **
|
|
181
|
+
Request/response shapes live at the canonical docs URL; this is the index of what to reach for. `${ACC}` = `CLOUDFLARE_ACCOUNT_ID`, `${ZONE}` = the zone id, `${TOKEN}` = a **reused per-scope narrow** token.
|
|
176
182
|
|
|
177
|
-
### Token create / verify
|
|
183
|
+
### Token list / create / verify / revoke
|
|
178
184
|
The endpoints below are the **account-scoped** (`cfat_…`) family — the common case. For a **user-scoped** (`cfut_…`) master, swap to the `/user/tokens/…` family per § Token type — route endpoints by prefix; the account endpoints return `9109` for that token type.
|
|
179
|
-
- `GET /accounts/${ACC}/tokens/verify` — confirm an **account-scoped** (`cfat_…`) token is active. This is the endpoint for the master and every
|
|
180
|
-
- `
|
|
185
|
+
- `GET /accounts/${ACC}/tokens/verify` — confirm an **account-scoped** (`cfat_…`) token is active. This is the endpoint for the master and every per-scope narrow token here, all of which are account-scoped. **`/user/tokens/verify` is user-scoped only** and returns `Invalid API Token` for a `cfat_…` token even when it is valid — do not use it to verify an account token.
|
|
186
|
+
- `GET /accounts/${ACC}/tokens` — list all tokens on the account (id, name, status, `expires_on`). Use it to find a per-scope token to reuse and to enumerate strays before a reconcile. User-scoped twin: `GET /user/tokens`.
|
|
187
|
+
- `POST /accounts/${ACC}/tokens` — mint a per-scope token (see § Provisioning and reusing a stable per-scope token). Mint only when the list above shows the scope absent.
|
|
188
|
+
- `DELETE /accounts/${ACC}/tokens/{id}` — revoke a token by id (the reconcile/cleanup verb). User-scoped twin: `DELETE /user/tokens/{id}`. Route by the master's prefix exactly as verify/mint do — `cfat_…` → `/accounts/${ACC}/tokens…`, `cfut_…` → `/user/tokens…`; the cross-type call returns `9109`.
|
|
181
189
|
- `GET /accounts/${ACC}/tokens/permission_groups` — resolve permission-group ids for scoping.
|
|
182
190
|
|
|
183
191
|
### DNS records (zone-scoped; needs **Zone · DNS · Edit**)
|
|
@@ -205,10 +213,41 @@ curl -sS -X POST -H "Authorization: Bearer ${TOKEN}" -H "Content-Type: applicati
|
|
|
205
213
|
|
|
206
214
|
---
|
|
207
215
|
|
|
216
|
+
## Reconcile — the standing cleanup check
|
|
217
|
+
|
|
218
|
+
Token accumulation **emits no event and does not reproduce on demand**, so the decay mechanism is a standing reconcile pass, run **periodically and before a deploy** — not a one-off. It lists every token, keeps the known-good set, and revokes the rest. The known-good set is: the **master**, the stable per-scope operation tokens (`<brand>-pages-d1`, `<brand>-dns`, `<brand>-access`), and the **tunnel** tokens. Everything else — throwaway, duplicate-named, stray — is revoked.
|
|
219
|
+
|
|
220
|
+
```bash
|
|
221
|
+
set -a; . "${SECRETS_DIR}/cloudflare.env"; set +a
|
|
222
|
+
: "${CLOUDFLARE_API_TOKEN:?credentials not loaded}"; : "${CLOUDFLARE_ACCOUNT_ID:?account id not loaded}"
|
|
223
|
+
LIST_URL="https://api.cloudflare.com/client/v4/accounts/${CLOUDFLARE_ACCOUNT_ID}/tokens" # /user/tokens for a cfut_ master
|
|
224
|
+
|
|
225
|
+
# Names kept verbatim — adjust to this account's stable set + tunnel token names.
|
|
226
|
+
KEEP="<brand>-pages-d1 <brand>-dns <brand>-access maxy-api-master maxy-pages Maxy"
|
|
227
|
+
|
|
228
|
+
# 1. List; for each token whose name is NOT in KEEP, revoke it by id.
|
|
229
|
+
curl -sS -H "Authorization: Bearer ${CLOUDFLARE_API_TOKEN}" "${LIST_URL}?per_page=100" \
|
|
230
|
+
| jq -r '.result[] | [.id, .name] | @tsv' \
|
|
231
|
+
| while IFS=$'\t' read -r id name; do
|
|
232
|
+
case " ${KEEP} " in *" ${name} "*) echo "keep ${name}";;
|
|
233
|
+
*) curl -sS -X DELETE -H "Authorization: Bearer ${CLOUDFLARE_API_TOKEN}" "${LIST_URL}/${id}" >/dev/null
|
|
234
|
+
echo "revoke ${name}";; esac
|
|
235
|
+
done
|
|
236
|
+
|
|
237
|
+
# 2. Post-condition — re-list and confirm the revoked names are gone. Do not trust the DELETE exit code.
|
|
238
|
+
curl -sS -H "Authorization: Bearer ${CLOUDFLARE_API_TOKEN}" "${LIST_URL}?per_page=100" | jq -r '.result[].name'
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
`?per_page=100` returns the first page only; an account that has accumulated **more than 100 tokens** — the exact failure this pass exists to undo — needs `&page=2`, `&page=3`, … until a short page returns. Reconcile is the accumulation-survival mechanism, so it must walk every page, not just the first.
|
|
242
|
+
|
|
243
|
+
The tally is **names only** — a token value is never printed (the redaction binding in § Provisioning applies). "Revoked N" is a claim only once the follow-up list (step 2) shows those names absent, never an inference from the DELETE call's exit code.
|
|
244
|
+
|
|
245
|
+
---
|
|
246
|
+
|
|
208
247
|
## When to use which path
|
|
209
248
|
|
|
210
249
|
- **Tunnel create/route/run** → `cloudflared` (OAuth cert), per `manual-setup.md`. The API can manage tunnels too, but the cert path is the established one.
|
|
211
250
|
- **Apex CNAME** → API (above) or dashboard (`dashboard-guide.md`). Either flattens correctly.
|
|
212
|
-
- **Pages deploy** → `wrangler` with
|
|
213
|
-
- **D1 read/write** → `wrangler d1 execute --remote` with
|
|
214
|
-
- **Inspecting account state** (which zones, which tunnels) → API `GET` with a read-scoped
|
|
251
|
+
- **Pages deploy** → `wrangler` with the reused `<brand>-pages-d1` token, per `hosting-sites.md`.
|
|
252
|
+
- **D1 read/write** → `wrangler d1 execute --remote` with the reused D1-Edit token, per `d1-data-capture.md`.
|
|
253
|
+
- **Inspecting account state** (which zones, which tunnels) → API `GET` with a read-scoped token (reused), or the dashboard.
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
This is the established pattern for turning a static site's contact / waitlist form into durable, queryable leads: the form POSTs to a **Pages Function**, the Function inserts a row into a **Cloudflare D1** database, and the agent reads and sweeps new rows with `wrangler d1 execute --remote`. It is live on `realagent.pages.dev` today — the worked example below is that deployment, generalized with placeholders.
|
|
4
4
|
|
|
5
|
-
Pair this with `hosting-sites.md` (how the site itself gets deployed) and `api.md` (the master token +
|
|
5
|
+
Pair this with `hosting-sites.md` (how the site itself gets deployed) and `api.md` (the master token + reused per-scope token discipline that authenticates every `wrangler` call).
|
|
6
6
|
|
|
7
7
|
---
|
|
8
8
|
|
|
@@ -13,47 +13,53 @@ A Pages-only token **cannot touch D1**. The deploy succeeds, the form renders, a
|
|
|
13
13
|
- **Account · Cloudflare Pages · Edit**
|
|
14
14
|
- **Account · D1 · Edit**
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
Use the stable `<brand>-pages-d1` token that includes both permission groups (see `api.md` § Provisioning and reusing a stable per-scope token, and § 0 below) before any `wrangler d1` or D1-backed deploy command. This was observed live in the session that built `realagent.pages.dev`; it is the first thing to check when captures stop arriving.
|
|
17
17
|
|
|
18
18
|
**Even a read-only `SELECT` needs D1 *Edit*.** The `wrangler d1 execute --remote` query path goes through the D1 **query** endpoint, which **rejects a D1-Read token** — a token scoped to D1 *Read* fails the `SELECT`, not just the `INSERT`/`UPDATE`. Use the **D1-Edit**-scoped token (or the both-Pages-Edit-and-D1-Edit token above) for every `wrangler d1 execute`, reads included. A separately-minted "D1 Read" token for the sweep `SELECT`s is wrong guidance: it is rejected at the query endpoint.
|
|
19
19
|
|
|
20
20
|
---
|
|
21
21
|
|
|
22
|
-
## 0.
|
|
22
|
+
## 0. Load (or mint once) the stable Pages-+-D1 token
|
|
23
23
|
|
|
24
|
-
Every command below uses `${
|
|
24
|
+
Every command below uses `${PAGES_D1}` — the **stable, reused** per-scope token (`<brand>-pages-d1`) scoped to **both** Pages Edit and D1 Edit. Provision it the way `api.md` § Provisioning and reusing a stable per-scope token prescribes: **load it from the secrets file; mint it once only if absent** (no expiry), persist it, and reuse it thereafter. It is never written into a project tree or echoed:
|
|
25
25
|
|
|
26
26
|
```bash
|
|
27
|
-
set -a; . "${SECRETS_DIR}/cloudflare.env"; set +a # loads master + account id
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
27
|
+
set -a; . "${SECRETS_DIR}/cloudflare.env"; set +a # loads master + account id + persisted per-scope tokens
|
|
28
|
+
: "${CLOUDFLARE_API_TOKEN:?credentials not loaded}"; : "${CLOUDFLARE_ACCOUNT_ID:?account id not loaded}"
|
|
29
|
+
|
|
30
|
+
TOKEN_KEY="CF_PAGES_D1_TOKEN"; SCOPE_NAME="<brand>-pages-d1"
|
|
31
|
+
PAGES_D1=$(grep -m1 "^${TOKEN_KEY}=" "${SECRETS_DIR}/cloudflare.env" 2>/dev/null | cut -d= -f2-)
|
|
32
|
+
if [ -z "${PAGES_D1}" ]; then # absent → mint once, scoped to BOTH Pages Edit + D1 Edit, no expiry
|
|
33
|
+
PAGES_D1=$(curl -sS -X POST \
|
|
34
|
+
-H "Authorization: Bearer ${CLOUDFLARE_API_TOKEN}" \
|
|
35
|
+
-H "Content-Type: application/json" \
|
|
36
|
+
"https://api.cloudflare.com/client/v4/accounts/${CLOUDFLARE_ACCOUNT_ID}/tokens" \
|
|
37
|
+
--data @- <<JSON | jq -r '.result.value'
|
|
33
38
|
{
|
|
34
|
-
"name": "
|
|
39
|
+
"name": "${SCOPE_NAME}",
|
|
35
40
|
"policies": [{
|
|
36
41
|
"effect": "allow",
|
|
37
|
-
"resources": { "com.cloudflare.api.account
|
|
42
|
+
"resources": { "com.cloudflare.api.account.${CLOUDFLARE_ACCOUNT_ID}": "*" },
|
|
38
43
|
"permission_groups": [
|
|
39
44
|
{ "id": "<pages-edit-permission-group-id>", "name": "Pages Write" },
|
|
40
45
|
{ "id": "<d1-edit-permission-group-id>", "name": "D1 Write" }
|
|
41
46
|
]
|
|
42
|
-
}]
|
|
43
|
-
"expires_on": "<iso8601-a-few-minutes-out>"
|
|
47
|
+
}]
|
|
44
48
|
}
|
|
45
49
|
JSON
|
|
46
|
-
)
|
|
50
|
+
)
|
|
51
|
+
( umask 077; printf '%s=%s\n' "${TOKEN_KEY}" "${PAGES_D1}" >> "${SECRETS_DIR}/cloudflare.env" )
|
|
52
|
+
fi
|
|
47
53
|
```
|
|
48
54
|
|
|
49
|
-
Resolve the two permission-group ids once via `GET /accounts/{account_id}/tokens/permission_groups` (see `api.md`).
|
|
55
|
+
Resolve the two permission-group ids once via `GET /accounts/{account_id}/tokens/permission_groups` (see `api.md`). There is **no `unset`** — `${PAGES_D1}` is the reused per-scope token, not a throwaway; the reconcile pass in `api.md` § Reconcile keeps the account from accumulating strays.
|
|
50
56
|
|
|
51
57
|
---
|
|
52
58
|
|
|
53
59
|
## 1. Create the database
|
|
54
60
|
|
|
55
61
|
```bash
|
|
56
|
-
CLOUDFLARE_API_TOKEN="${
|
|
62
|
+
CLOUDFLARE_API_TOKEN="${PAGES_D1}" wrangler d1 create <db-name>
|
|
57
63
|
# worked example: wrangler d1 create realagent-leads
|
|
58
64
|
```
|
|
59
65
|
|
|
@@ -78,7 +84,7 @@ database_id = "<database_id>" # from step 1
|
|
|
78
84
|
Apply the schema to the remote database (`--remote` targets the live D1, not a local replica):
|
|
79
85
|
|
|
80
86
|
```bash
|
|
81
|
-
CLOUDFLARE_API_TOKEN="${
|
|
87
|
+
CLOUDFLARE_API_TOKEN="${PAGES_D1}" wrangler d1 execute <db-name> --remote --command \
|
|
82
88
|
"CREATE TABLE IF NOT EXISTS leads (
|
|
83
89
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
84
90
|
name TEXT,
|
|
@@ -132,14 +138,14 @@ Deploy via `hosting-sites.md`. The binding in `wrangler.toml` is what wires the
|
|
|
132
138
|
Read everything not yet ingested:
|
|
133
139
|
|
|
134
140
|
```bash
|
|
135
|
-
CLOUDFLARE_API_TOKEN="${
|
|
141
|
+
CLOUDFLARE_API_TOKEN="${PAGES_D1}" wrangler d1 execute <db-name> --remote --json --command \
|
|
136
142
|
"SELECT id, name, email, message, created_at FROM leads WHERE swept = 0 ORDER BY id;"
|
|
137
143
|
```
|
|
138
144
|
|
|
139
145
|
After ingesting those rows (e.g. writing them into the graph), mark them swept so the next read returns only newer ones:
|
|
140
146
|
|
|
141
147
|
```bash
|
|
142
|
-
CLOUDFLARE_API_TOKEN="${
|
|
148
|
+
CLOUDFLARE_API_TOKEN="${PAGES_D1}" wrangler d1 execute <db-name> --remote --command \
|
|
143
149
|
"UPDATE leads SET swept = 1 WHERE swept = 0;"
|
|
144
150
|
```
|
|
145
151
|
|
|
@@ -152,7 +158,7 @@ D1 capture is done when a **test POST to the live form** appears in the unswept
|
|
|
152
158
|
```bash
|
|
153
159
|
curl -sS -X POST -d "email=test@example.com&name=verify" "https://<project>.pages.dev/api/contact" -i | head -1
|
|
154
160
|
# then:
|
|
155
|
-
CLOUDFLARE_API_TOKEN="${
|
|
161
|
+
CLOUDFLARE_API_TOKEN="${PAGES_D1}" wrangler d1 execute <db-name> --remote --command \
|
|
156
162
|
"SELECT email FROM leads WHERE swept = 0;"
|
|
157
163
|
```
|
|
158
164
|
|
|
@@ -109,8 +109,9 @@ Use this when you need to audit which hostnames are pointing at which `<UUID>.cf
|
|
|
109
109
|
|
|
110
110
|
When the agent adds an SSH or SMB ingress hostname (per `references/manual-setup.md`),
|
|
111
111
|
the Access policy must exist before off-LAN clients can reach the Pi. There are two
|
|
112
|
-
paths: the agent can author it via the Cloudflare API with
|
|
113
|
-
token (see `references/api.md` § Access
|
|
112
|
+
paths: the agent can author it via the Cloudflare API with the reused Access-scoped
|
|
113
|
+
token (`<brand>-access`, minted once if absent — see `references/api.md` § Access and
|
|
114
|
+
§ Provisioning and reusing a stable per-scope token), or the operator can author it by hand in the
|
|
114
115
|
dashboard with the click-path below. `cloudflared` CLI has no Access-application create
|
|
115
116
|
subcommand, so the dashboard path is the manual alternative to the API.
|
|
116
117
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Deploying a site to Cloudflare Pages
|
|
2
2
|
|
|
3
|
-
How to put a static or Next.js site on Cloudflare Pages via `wrangler`, the way `realagent.pages.dev` is deployed. This covers the build → deploy → verify loop and the Next.js specifics. Auth is
|
|
3
|
+
How to put a static or Next.js site on Cloudflare Pages via `wrangler`, the way `realagent.pages.dev` is deployed. This covers the build → deploy → verify loop and the Next.js specifics. Auth is the reused per-scope token per `api.md`; if the site also captures form data, read `d1-data-capture.md` for the dual Pages-Edit **and** D1-Edit scope.
|
|
4
4
|
|
|
5
5
|
This is distinct from `references/serving-published-sites.md`, which serves a platform-published site at a custom domain through the brand's **cloudflared tunnel**. This reference is about **Cloudflare Pages** — Cloudflare hosts the build, not the install device.
|
|
6
6
|
|
|
@@ -31,19 +31,21 @@ compatibility_flags = ["nodejs_compat"] # required for next-on-pages and most F
|
|
|
31
31
|
|
|
32
32
|
If the site captures form data, add the `[[d1_databases]]` binding from `d1-data-capture.md` here.
|
|
33
33
|
|
|
34
|
-
## 3.
|
|
34
|
+
## 3. Load the reused deploy token
|
|
35
35
|
|
|
36
|
-
A Pages deploy needs **Account · Cloudflare Pages · Edit**. If the site has Pages Functions hitting D1, the token also needs **Account · D1 · Edit** (the single most common breakage — see `d1-data-capture.md`).
|
|
36
|
+
A Pages deploy needs **Account · Cloudflare Pages · Edit**. If the site has Pages Functions hitting D1, the token also needs **Account · D1 · Edit** (the single most common breakage — see `d1-data-capture.md`). Load the stable per-scope token — minting it once only if absent — per `api.md` § Provisioning and reusing a stable per-scope token:
|
|
37
37
|
|
|
38
38
|
```bash
|
|
39
39
|
set -a; . "${SECRETS_DIR}/cloudflare.env"; set +a
|
|
40
|
-
|
|
40
|
+
: "${CLOUDFLARE_API_TOKEN:?credentials not loaded}"; : "${CLOUDFLARE_ACCOUNT_ID:?account id not loaded}"
|
|
41
|
+
# PAGES_D1 = the reused per-scope token (Pages Edit, + D1 Edit if the site uses D1),
|
|
42
|
+
# loaded from cloudflare.env and minted once if absent — see api.md § Provisioning and reusing.
|
|
41
43
|
```
|
|
42
44
|
|
|
43
45
|
## 4. Deploy
|
|
44
46
|
|
|
45
47
|
```bash
|
|
46
|
-
CLOUDFLARE_API_TOKEN="${
|
|
48
|
+
CLOUDFLARE_API_TOKEN="${PAGES_D1}" wrangler pages deploy <output-dir> \
|
|
47
49
|
--project-name <project> --branch=main
|
|
48
50
|
```
|
|
49
51
|
|
|
@@ -295,8 +295,9 @@ rather than failing.
|
|
|
295
295
|
|
|
296
296
|
**Access policy — API or dashboard.** `cloudflared` CLI has no
|
|
297
297
|
Access-application create subcommand, so author the policy either via
|
|
298
|
-
the Cloudflare API with
|
|
299
|
-
`references/api.md` § Access) or by hand in
|
|
298
|
+
the Cloudflare API with the reused Access-scoped token (`<brand>-access`,
|
|
299
|
+
minted once if absent — see `references/api.md` § Access) or by hand in
|
|
300
|
+
the dashboard. After the
|
|
300
301
|
ingress is in place, the dashboard path is:
|
|
301
302
|
|
|
302
303
|
```
|
|
@@ -14,7 +14,7 @@ This is the entry point for every Cloudflare task on the install. Pick the opera
|
|
|
14
14
|
| Stand up / diagnose / reset a tunnel so a hostname is reachable | `references/manual-setup.md` | external `curl -I https://<hostname>` → `200` |
|
|
15
15
|
| Switch accounts, edit an apex CNAME by hand, or other dashboard-only clicks | `references/dashboard-guide.md` | the click-path completed in the operator's browser |
|
|
16
16
|
| Tear down corrupt tunnel state and start clean | `references/reset-guide.md` | a fresh `manual-setup.md` run reaches `200` |
|
|
17
|
-
| Call the Cloudflare API (DNS, zones, tunnels, Access, token
|
|
17
|
+
| Call the Cloudflare API (DNS, zones, tunnels, Access, token list/reuse/revoke) | `references/api.md` | the API call returns success and the change is observable |
|
|
18
18
|
| Deploy a static / Next.js site to Cloudflare Pages | `references/hosting-sites.md` | the deployed URL serves the new build |
|
|
19
19
|
| Capture form/waitlist submissions into a database | `references/d1-data-capture.md` | a test POST appears in `SELECT … WHERE swept = 0` |
|
|
20
20
|
| Diagnose / enable Web Analytics on a site (0 visitors, no data) | `references/web-analytics.md` | beacon present in live HTML (`curl -s https://<host>/ \| grep cloudflareinsights`) |
|
|
@@ -22,9 +22,9 @@ This is the entry point for every Cloudflare task on the install. Pick the opera
|
|
|
22
22
|
## Two auth paths, by operation class
|
|
23
23
|
|
|
24
24
|
- **Tunnel auth is OAuth.** `cloudflared tunnel login` writes `cert.pem`; the operator clicks the linkified OAuth URL in their own browser. This is the only path for tunnel create/route/run. See `references/manual-setup.md`.
|
|
25
|
-
- **API auth is the master token.** The operator provisions one fully-scoped master token once (an **advanced**, operator-guided dashboard step — the agent does not automate it) and stores it at the account-scoped secrets file. The agent reads the master only to **
|
|
25
|
+
- **API auth is the master token.** The operator provisions one fully-scoped master token once (an **advanced**, operator-guided dashboard step — the agent does not automate it) and stores it at the account-scoped secrets file. The agent reads the master only to **provision one stable narrow token per scope** — `<brand>-pages-d1`, `<brand>-dns`, `<brand>-access` — loading it from the secrets file, minting it once if absent (no expiry), persisting it, and reusing it for every later operation in that scope. A standing **reconcile** pass revokes strays. See `references/api.md` for the storage convention, the provisioning-and-reuse flow, the reconcile pass, and the binding redaction discipline.
|
|
26
26
|
|
|
27
|
-
Neither the master token nor any
|
|
27
|
+
Neither the master token nor any per-scope token is ever written into a project tree, committed, or echoed into chat — the per-scope tokens persist only in the account-scoped secrets file, never in a deployable project tree.
|
|
28
28
|
|
|
29
29
|
## Outcome contracts — what "done" means
|
|
30
30
|
|
|
@@ -55,7 +55,7 @@ After the service is up, the agent runs `curl -I https://<admin-hostname>` and p
|
|
|
55
55
|
|
|
56
56
|
## Apex hostnames
|
|
57
57
|
|
|
58
|
-
The `cloudflared` CLI cannot route apex records (e.g. `maxy.chat`) — standard DNS forbids a CNAME at the zone apex. There are now two ways to create the apex record: the operator edits the apex CNAME in the dashboard (`references/dashboard-guide.md` § "Edit an apex CNAME"), or the agent creates it via the Cloudflare API with
|
|
58
|
+
The `cloudflared` CLI cannot route apex records (e.g. `maxy.chat`) — standard DNS forbids a CNAME at the zone apex. There are now two ways to create the apex record: the operator edits the apex CNAME in the dashboard (`references/dashboard-guide.md` § "Edit an apex CNAME"), or the agent creates it via the Cloudflare API with the reused DNS-scoped token (`<brand>-dns`, minted once if absent — `references/api.md` § DNS records). Either reaches the same flattened-CNAME result; pick the dashboard path when the operator prefers to click, the API path when an end-to-end agent run is wanted.
|
|
59
59
|
|
|
60
60
|
## Reset
|
|
61
61
|
|
|
@@ -66,10 +66,10 @@ When local Cloudflare state is corrupt or the operator wants to start from a kno
|
|
|
66
66
|
When the operator's request touches Cloudflare, the agent's permitted actions are:
|
|
67
67
|
|
|
68
68
|
- Invoke `cloudflared` via Bash, following `references/manual-setup.md`.
|
|
69
|
-
- Invoke `wrangler` and the Cloudflare API via Bash, following `references/api.md`, `references/hosting-sites.md`, and `references/d1-data-capture.md`, using
|
|
69
|
+
- Invoke `wrangler` and the Cloudflare API via Bash, following `references/api.md`, `references/hosting-sites.md`, and `references/d1-data-capture.md`, using the reused per-scope narrow token (minted once if absent, then reused).
|
|
70
70
|
- Write `config.yml` and `alias-domains.json` per the runbook.
|
|
71
71
|
- Install and start the brand's cloudflared user service.
|
|
72
72
|
- Quote `references/manual-setup.md`, `references/reset-guide.md`, or `references/dashboard-guide.md` verbatim when a dashboard click-path is the right tool.
|
|
73
73
|
- Verify reachability via `curl -I https://<hostname>` and surface the response.
|
|
74
74
|
|
|
75
|
-
The agent does not drive the dashboard via Playwright or Chrome DevTools, and does not browser-automate master-token creation. It does not synthesise `cloudflared` flag combinations from web search or training — every command comes from the runbook. It never writes a token to disk in a project tree, commits one, or prints one into chat. When a step fails, the agent reports the exact output, names the recovery step from `references/reset-guide.md`, and stops. Improvisation — "let me try a different flag"
|
|
75
|
+
The agent does not drive the dashboard via Playwright or Chrome DevTools, and does not browser-automate master-token creation. It does not synthesise `cloudflared` flag combinations from web search or training — every command comes from the runbook. **Cloudflare API calls are made with `curl` + `jq` only — never a Python or node script.** It never writes a token to disk in a project tree, commits one, or prints one into chat. When a step fails, the agent reports the exact output, names the recovery step from `references/reset-guide.md`, and stops. Improvisation — "let me try a different flag", "let me drive the dashboard myself", or "let me write a quick Python script for this API call" — is the behaviour this rule exists to prevent.
|
|
@@ -19,13 +19,13 @@ The Telegram plugin connects {{productName}} to a Telegram bot. Once set up, you
|
|
|
19
19
|
|
|
20
20
|
### Step 2: Connect the plugin
|
|
21
21
|
|
|
22
|
-
Tell {{productName}}: "Set up Telegram" or "Configure the Telegram bot."
|
|
22
|
+
Tell {{productName}}: "Set up Telegram" or "Configure the Telegram bot." Setup runs entirely in the admin chat — there is no settings page.
|
|
23
23
|
|
|
24
|
-
{{productName}}
|
|
24
|
+
{{productName}} asks for your bot token, then asks whether this is an **admin bot** (only your numeric Telegram IDs may message it) or a **public bot** (customer-facing, with a DM policy). It verifies the token, registers the bot's webhook on your brand's edge, stores the token under your brand's config (never in a shared system directory), and — for a public bot — sets the agent that will answer. The bot is now connected.
|
|
25
25
|
|
|
26
26
|
### Step 3: Start the bot
|
|
27
27
|
|
|
28
|
-
In Telegram, open your bot and send `/start
|
|
28
|
+
In Telegram, open your bot and send `/start`, then a message. {{productName}} receives it and replies in the same chat. Admin-bot messages from your registered IDs reach the admin agent; public-bot messages reach the public agent you selected.
|
|
29
29
|
|
|
30
30
|
## Sending Messages via {{productName}}
|
|
31
31
|
|
|
@@ -44,10 +44,15 @@ Walks users through connecting Telegram bots. Covers bot creation, token configu
|
|
|
44
44
|
- Multi-bot support — separate admin and public bots with different access policies
|
|
45
45
|
- Admin routing — restrict a bot to admin-only access via numeric user ID bindings
|
|
46
46
|
|
|
47
|
+
## Setup
|
|
48
|
+
|
|
49
|
+
Connecting a Telegram bot runs through the `telegram:configure` skill (`skills/configure/SKILL.md`) — the brand-aware, PTY-only setup flow. It is the maxy `telegram:configure`; on `-code` brands the upstream `claude-plugins-official/telegram` plugin is not installed, so there is no shadowing (Task 727). The skill stores the BotFather token under the brand install, registers the webhook on the `/api/telegram/` edge path, sets access policy, and selects the responding agent.
|
|
50
|
+
|
|
47
51
|
## References
|
|
48
52
|
|
|
49
53
|
| Task | When to use | Reference |
|
|
50
54
|
|------|-------------|-----------|
|
|
51
|
-
| Guided setup | User wants help connecting Telegram | `
|
|
55
|
+
| Guided setup | User wants help connecting Telegram | `skills/configure/SKILL.md` |
|
|
56
|
+
| BotFather / user-ID / admin-vs-public detail | Operator needs the underlying detail | `references/setup-guide.md` |
|
|
52
57
|
|
|
53
|
-
Load the
|
|
58
|
+
Load the skill and follow its instructions.
|