@rubytech/create-maxy-code 0.1.244 → 0.1.246

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/package.json +1 -1
  2. package/payload/platform/docs/superpowers/plans/2026-06-04-public-agent-knowledge-delivery.md +230 -0
  3. package/payload/platform/plugins/admin/mcp/dist/index.js +1 -1
  4. package/payload/platform/plugins/admin/mcp/dist/index.js.map +1 -1
  5. package/payload/platform/plugins/admin/skills/platform-architecture/SKILL.md +16 -16
  6. package/payload/platform/plugins/admin/skills/public-agent-manager/SKILL.md +32 -56
  7. package/payload/platform/plugins/cloudflare/.claude-plugin/plugin.json +1 -1
  8. package/payload/platform/plugins/cloudflare/PLUGIN.md +10 -7
  9. package/payload/platform/plugins/cloudflare/references/api.md +166 -0
  10. package/payload/platform/plugins/cloudflare/references/d1-data-capture.md +157 -0
  11. package/payload/platform/plugins/cloudflare/references/dashboard-guide.md +6 -6
  12. package/payload/platform/plugins/cloudflare/references/hosting-sites.md +66 -0
  13. package/payload/platform/plugins/cloudflare/references/manual-setup.md +5 -3
  14. package/payload/platform/plugins/cloudflare/skills/cloudflare/SKILL.md +72 -0
  15. package/payload/platform/plugins/docs/references/cloudflare.md +4 -4
  16. package/payload/platform/plugins/docs/references/deployment.md +1 -1
  17. package/payload/platform/scripts/setup-account.sh +16 -8
  18. package/payload/platform/services/claude-session-manager/dist/jsonl-tail.d.ts +12 -0
  19. package/payload/platform/services/claude-session-manager/dist/jsonl-tail.d.ts.map +1 -1
  20. package/payload/platform/services/claude-session-manager/dist/jsonl-tail.js +1 -1
  21. package/payload/platform/services/claude-session-manager/dist/jsonl-tail.js.map +1 -1
  22. package/payload/platform/services/claude-session-manager/dist/pty-spawner.d.ts +1 -1
  23. package/payload/platform/services/claude-session-manager/dist/pty-spawner.d.ts.map +1 -1
  24. package/payload/platform/services/claude-session-manager/dist/pty-spawner.js +31 -5
  25. package/payload/platform/services/claude-session-manager/dist/pty-spawner.js.map +1 -1
  26. package/payload/platform/services/claude-session-manager/dist/system-prompt.d.ts +2 -1
  27. package/payload/platform/services/claude-session-manager/dist/system-prompt.d.ts.map +1 -1
  28. package/payload/platform/services/claude-session-manager/dist/system-prompt.js +39 -6
  29. package/payload/platform/services/claude-session-manager/dist/system-prompt.js.map +1 -1
  30. package/payload/platform/templates/specialists/agents/personal-assistant.md +2 -2
  31. package/payload/server/{chunk-SRO5RFMV.js → chunk-JMEX5NRX.js} +1 -3
  32. package/payload/server/maxy-edge.js +1 -1
  33. package/payload/server/server.js +1 -1
  34. package/payload/platform/plugins/cloudflare/skills/setup-tunnel/SKILL.md +0 -55
  35. package/payload/platform/templates/agents/public/SOUL.md +0 -19
  36. package/payload/platform/templates/agents/public/config.json +0 -8
@@ -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:849e817f1959f8b45a530e2d28f3eaa9a3fc7ad66729747d92087517ae7e3fe8
4
+ content-hash: sha256:bff8fac482b70e56a1d4dd32167987663f0282ce43f4659d9c8c5cb592f77cd9
5
5
  brand: maxy-code
6
6
  product-name: Maxy
7
7
  ---
@@ -645,7 +645,7 @@ macOS is the lightweight surface. Compared with the Pi install, the macOS path d
645
645
 
646
646
  - **No cgroup / resource decoupling.** Pi installs decouple Claude Code's cgroup from systemd's session scope so a closed VNC viewer cannot reap the long-running agent. macOS uses launchd, which is already per-user and does not have the same cleanup pathology, so the work is unnecessary.
647
647
  - **No VNC.** The admin UI is the surface. You drive it from a browser on the same machine or any device on the LAN; there is no display server to bootstrap.
648
- - **No `cloudflared` tunnel by default.** Pi installs ship a tunnel because the device is typically headless and on a residential network. On a Mac the LAN URL is usually enough; if you want a public URL, install `cloudflared` separately and run `cloudflared tunnel login` from the terminal. Cloudflare API tokens are never used — only the CLI's interactive `tunnel login` flow.
648
+ - **No `cloudflared` tunnel by default.** Pi installs ship a tunnel because the device is typically headless and on a residential network. On a Mac the LAN URL is usually enough; if you want a public URL, install `cloudflared` separately and run `cloudflared tunnel login` from the terminal. The tunnel uses the CLI's interactive `tunnel login` flow; other Cloudflare operations (DNS, Pages, D1) use the API with a token the agent mints from an operator-provisioned master.
649
649
 
650
650
  ## Smoke checklist
651
651
 
@@ -677,7 +677,7 @@ The doc is brand-aware. Examples use the default brand `maxy-code`; substitute `
677
677
  - Raspberry Pi 5, 16GB RAM (canonical) — Pi 4 8GB works but the first install runs slower.
678
678
  - Ubuntu Server 24.04 LTS, 64-bit, freshly imaged with Raspberry Pi Imager. Earlier Ubuntu / Pi OS releases are not part of the supported matrix.
679
679
  - The pi has a wired or Wi-Fi route to the internet and an SSH-reachable user with sudo (the username does not matter — Rubytech images ship `admin` by default).
680
- - A Cloudflare account whose dashboard you can sign into in a web browser. No API tokens are ever issued or stored; the only Cloudflare auth path is `cloudflared tunnel login` running in the Pi's VNC browser after install.
680
+ - A Cloudflare account whose dashboard you can sign into in a web browser. The tunnel signs in via `cloudflared tunnel login` (OAuth, in the Pi's VNC browser after install); DNS/Pages/D1 use the Cloudflare API with a token the agent mints from a master token you provision once.
681
681
  - A connected monitor or a working VNC viewer for the one-time `cloudflared tunnel login` step. After that step the Pi runs headless.
682
682
 
683
683
  For Hetzner Cloud, see [hetzner.md](hetzner.md). The apt path, systemd user-service, and Cloudflare flow are the same; the difference is that a cloud VM has no physical display and no LAN to the operator, so the noVNC browser is reached over SSH port-forwarding for the one-time Cloudflare bootstrap.
@@ -749,9 +749,9 @@ The installer also wires `loginctl enable-linger <user>` so the user-service sur
749
749
 
750
750
  ## 4. Bootstrap the Cloudflare tunnel
751
751
 
752
- The installer puts `cloudflared` on PATH but does not provision the tunnel — Cloudflare auth happens once, interactively, in a browser the operator drives. There is no API token, no service token, no SDK call: the only auth path is `cloudflared tunnel login`, which writes a browser-issued cert to `$HOME/.maxy-code/.cloudflared/cert.pem` on success.
752
+ The installer puts `cloudflared` on PATH but does not provision the tunnel — Cloudflare tunnel auth happens once, interactively, in a browser the operator drives. The tunnel's only auth path is `cloudflared tunnel login`, which writes a browser-issued cert to `$HOME/.maxy-code/.cloudflared/cert.pem` on success. Other Cloudflare operations (DNS, Pages, D1) use the API with a token the agent mints from an operator-provisioned master — see the `cloudflare` plugin.
753
753
 
754
- Open the Pi's VNC browser at `http://<hostname>.local:<port>/vnc` (or over the LAN at whichever port the install log printed for noVNC). In the chat surface, ask the agent to run the Cloudflare setup — the [`cloudflare`](../../platform/plugins/cloudflare/PLUGIN.md) plugin's `setup-tunnel` skill walks `cloudflared tunnel login`, `cloudflared tunnel create`, `cloudflared tunnel route dns`, and the systemd `<hostname>-cloudflared.service` unit in order, streaming `cloudflared`'s stdout verbatim into chat. The OAuth URL the CLI prints is linkified by the PTY; the operator clicks it inside the VNC browser and authorises the cert against the right Cloudflare account.
754
+ Open the Pi's VNC browser at `http://<hostname>.local:<port>/vnc` (or over the LAN at whichever port the install log printed for noVNC). In the chat surface, ask the agent to run the Cloudflare setup — the [`cloudflare`](../../platform/plugins/cloudflare/PLUGIN.md) plugin's `cloudflare` skill walks `cloudflared tunnel login`, `cloudflared tunnel create`, `cloudflared tunnel route dns`, and the systemd `<hostname>-cloudflared.service` unit in order, streaming `cloudflared`'s stdout verbatim into chat. The OAuth URL the CLI prints is linkified by the PTY; the operator clicks it inside the VNC browser and authorises the cert against the right Cloudflare account.
755
755
 
756
756
  Setup is done when, and only when, `curl -I https://<hostname>.<your-zone>` issued from outside the local network returns `HTTP/2 200`. No state file, no `tunnel run` exit code, and no "service is active" claim substitutes for the live HTTPS response.
757
757
 
@@ -798,7 +798,7 @@ npx -y @rubytech/create-realagent-code@latest --uninstall
798
798
  ## What this install does not do
799
799
 
800
800
  - **No SCP / rsync.** The Pi is reached over npm only. Updates are `npx -y @rubytech/create-maxy-code@latest …` again, never a file push from the operator's laptop.
801
- - **No Cloudflare API tokens.** The only Cloudflare auth path is `cloudflared tunnel login` running in the Pi's VNC browser. If a doc, plugin, or workflow asks for a CF API token it is wrong surface the discrepancy before proceeding.
801
+ - **Tunnel auth is OAuth; the API is permitted for the rest.** The tunnel's only auth path is `cloudflared tunnel login` in the Pi's VNC browser. DNS, Pages, and D1 use the Cloudflare API with a short-lived narrow token the agent mints from an operator-provisioned master token (see the `cloudflare` plugin); tokens are never written to a project tree or echoed.
802
802
  - **No shared state across brands.** Two brands on one Pi each have their own Neo4j port, systemd unit, VNC display, websockify port, tunnel, and persist directory. They do not share DNS, ports, or filesystem state.
803
803
 
804
804
  ## Smoke checklist
@@ -807,7 +807,7 @@ Fresh-Pi smoke pass criteria:
807
807
 
808
808
  1. Install on a clean Ubuntu Server 24.04 image with no prior Maxy footprint completes, prints a LAN URL, and the systemd user-service is `active (running)`.
809
809
  2. The LAN URL `http://<hostname>.local:<port>` opens the admin UI and the chat surface is interactive.
810
- 3. Cloudflare setup driven by the `cloudflare` plugin's `setup-tunnel` skill ends with `curl -I https://<hostname>.<your-zone>` returning `HTTP/2 200` from outside the LAN.
810
+ 3. Cloudflare setup driven by the `cloudflare` plugin's `cloudflare` skill ends with `curl -I https://<hostname>.<your-zone>` returning `HTTP/2 200` from outside the LAN.
811
811
  4. Reboot — both URLs are reachable again after boot without any manual action.
812
812
  5. Install a second brand with a different `--hostname`; both brands' admin UIs are reachable on their own ports / public URLs and neither has touched the other's state.
813
813
  6. Uninstall removes the systemd unit, the Avahi service file, and the persist directory.
@@ -949,9 +949,9 @@ After the Cloudflare tunnel is provisioned, close the SSH session — every surf
949
949
 
950
950
  ## 5. Bootstrap the Cloudflare tunnel
951
951
 
952
- The installer puts `cloudflared` on PATH but does not provision the tunnel — Cloudflare auth happens once, interactively, in the noVNC browser the operator drives over the SSH forward from step 4. There is no API token, no service token, no SDK call: the only auth path is `cloudflared tunnel login`, which writes a browser-issued cert to `$HOME/.maxy-code/.cloudflared/cert.pem` on success.
952
+ The installer puts `cloudflared` on PATH but does not provision the tunnel — Cloudflare tunnel auth happens once, interactively, in the noVNC browser the operator drives over the SSH forward from step 4. The tunnel's only auth path is `cloudflared tunnel login`, which writes a browser-issued cert to `$HOME/.maxy-code/.cloudflared/cert.pem` on success. Other Cloudflare operations (DNS, Pages, D1) use the API with a token the agent mints from an operator-provisioned master — see the `cloudflare` plugin.
953
953
 
954
- In the noVNC browser session, open the admin UI at `http://localhost:<port>`. In chat, ask the agent to run the Cloudflare setup — the [`cloudflare`](../../platform/plugins/cloudflare/PLUGIN.md) plugin's `setup-tunnel` skill walks `cloudflared tunnel login`, `cloudflared tunnel create`, `cloudflared tunnel route dns`, and the systemd `<hostname>-cloudflared.service` unit in order, streaming `cloudflared`'s stdout verbatim into chat. The OAuth URL the CLI prints is linkified by the PTY; the operator clicks it inside the noVNC browser and authorises the cert against the right Cloudflare account.
954
+ In the noVNC browser session, open the admin UI at `http://localhost:<port>`. In chat, ask the agent to run the Cloudflare setup — the [`cloudflare`](../../platform/plugins/cloudflare/PLUGIN.md) plugin's `cloudflare` skill walks `cloudflared tunnel login`, `cloudflared tunnel create`, `cloudflared tunnel route dns`, and the systemd `<hostname>-cloudflared.service` unit in order, streaming `cloudflared`'s stdout verbatim into chat. The OAuth URL the CLI prints is linkified by the PTY; the operator clicks it inside the noVNC browser and authorises the cert against the right Cloudflare account.
955
955
 
956
956
  Setup is done when, and only when, `curl -I https://<hostname>.<your-zone>` issued from the operator's laptop returns `HTTP/2 200`. No state file, no `tunnel run` exit code, and no "service is active" claim substitutes for the live HTTPS response.
957
957
 
@@ -993,7 +993,7 @@ npx -y @rubytech/create-realagent-code@latest --uninstall
993
993
  ## What this install does not do
994
994
 
995
995
  - **No SCP / rsync.** Updates are `npx -y @rubytech/create-maxy-code@latest …` again, never a file push from the operator's laptop.
996
- - **No Cloudflare API tokens.** The only Cloudflare auth path is `cloudflared tunnel login` in the noVNC browser over SSH forward.
996
+ - **Tunnel auth is OAuth; the API is permitted for the rest.** The tunnel's only auth path is `cloudflared tunnel login` in the noVNC browser over SSH forward. DNS, Pages, and D1 use the Cloudflare API with a short-lived narrow token the agent mints from an operator-provisioned master token (see the `cloudflare` plugin).
997
997
  - **No shared state across brands.** Two brands on one server each have their own Neo4j port, systemd unit, VNC display, websockify port, tunnel, and persist directory.
998
998
  - **No public IPv4 exposure.** The Hetzner firewall opens port 22 only; every operator-facing surface is fronted by the Cloudflare tunnel.
999
999
 
@@ -1004,7 +1004,7 @@ Fresh-Hetzner smoke pass criteria:
1004
1004
  1. Provision a CAX31 with Ubuntu 24.04 arm64 and an SSH key; SSH in as `root`, create `admin`, switch.
1005
1005
  2. Install completes on the clean image, prints a loopback URL, and the systemd user-service is `active (running)`.
1006
1006
  3. The noVNC page reached over `ssh -L 8080:localhost:<novnc-port>` displays the admin UI.
1007
- 4. Cloudflare setup driven by the `cloudflare` plugin's `setup-tunnel` skill ends with `curl -I https://<hostname>.<your-zone>` returning `HTTP/2 200` from the operator's laptop.
1007
+ 4. Cloudflare setup driven by the `cloudflare` plugin's `cloudflare` skill ends with `curl -I https://<hostname>.<your-zone>` returning `HTTP/2 200` from the operator's laptop.
1008
1008
  5. Reboot the server; both `<hostname>.service` and `<hostname>-cloudflared.service` come back up; the public URL is reachable again without any manual action.
1009
1009
  6. Install a second brand with a different `--hostname`; both brands' admin UIs are reachable on their own public hostnames and neither has touched the other's state.
1010
1010
  7. Uninstall removes the systemd unit and the persist directory.
@@ -1017,7 +1017,7 @@ Source: https://docs.getmaxy.com/cloudflare.md
1017
1017
 
1018
1018
  # Cloudflare Tunnel — the dashboard is the source of truth
1019
1019
 
1020
- Each installation has its own Cloudflare account. Sign-in is OAuth: the agent invokes `cloudflared tunnel login` via Bash; the Cloudflare Authorize URL streams into the admin chat PTY and the native terminal renders it as a clickable link. Click it, authorise in your own browser, and `cloudflared` writes `cert.pem` to the brand's config directory. The agent never reads or mutates Cloudflare account state directly whatever you see in your logged-in dashboard is the single source of truth. When something needs doing on the account side (adding a domain, deleting a stray entry, switching accounts), the agent relays the click-paths; you run them in your browser.
1020
+ Each installation has its own Cloudflare account. The **tunnel** sign-in is OAuth: the agent invokes `cloudflared tunnel login` via Bash; the Cloudflare Authorize URL streams into the admin chat PTY and the native terminal renders it as a clickable link. Click it, authorise in your own browser, and `cloudflared` writes `cert.pem` to the brand's config directory. For **everything else** (DNS, Pages, D1, Access) the agent uses the Cloudflare API, authenticated by a short-lived narrow token it mints from a master token you provision once in the dashboard (an advanced step the agent never automates). Some account-side jobs — adding a domain, switching accounts are still easiest in your browser, and the agent relays those click-paths; the rest it can do directly via the API.
1021
1021
 
1022
1022
  ## Identity model
1023
1023
 
@@ -1025,10 +1025,10 @@ Each installation has its own Cloudflare account. Sign-in is OAuth: the agent in
1025
1025
  |------|--------|
1026
1026
  | **Product identity** (Maxy vs Real Agent) | `brand.json` (`productName`, `configDir`) — known at install. |
1027
1027
  | **Cloudflare account identity** | `cert.pem` from OAuth. One account per brand per device. |
1028
- | **Domain scope** (which zones the operator can route) | Live Cloudflare dashboard — the operator picks the zone in the dashboard during OAuth or names it in chat. The agent does not enumerate zones programmatically. |
1028
+ | **Domain scope** (which zones the operator can route) | The operator picks the zone in the dashboard during OAuth or names it in chat; the agent can also enumerate zones via the API with a minted read-scoped token. |
1029
1029
  | **Local tunnel state** | `~/{configDir}/cloudflared/` — `cert.pem`, `<UUID>.json`, `config.yml`, `alias-domains.json`. |
1030
1030
 
1031
- There is no token-based auth for the operator-owned path (Mode A). To switch Cloudflare accounts, the agent runs the reset flow from `plugins/cloudflare/references/reset-guide.md` (deletes the cert and every tunnel on the current account), then the manual-setup flow again — `cloudflared tunnel login` picks a fresh account when you sign in.
1031
+ Tunnel auth on the operator-owned path (Mode A) is the OAuth cert (`cert.pem`); API operations use a narrow token the agent mints from your master token. To switch Cloudflare accounts, the agent runs the reset flow from `plugins/cloudflare/references/reset-guide.md` (deletes the cert and every tunnel on the current account), then the manual-setup flow again — `cloudflared tunnel login` picks a fresh account when you sign in.
1032
1032
 
1033
1033
  ## Setup flow
1034
1034
 
@@ -1114,7 +1114,7 @@ The most common cause is wrong nameservers on the domain. The domain must use Cl
1114
1114
 
1115
1115
  **Does:** invokes `cloudflared` directly via Bash, following `plugins/cloudflare/references/manual-setup.md` step by step; quotes click-paths from the reference files verbatim; verifies external reachability with `curl -I` and surfaces the response.
1116
1116
 
1117
- **Does not:** drive the Cloudflare dashboard via Playwright, synthesise alternative `cloudflared` flag sequences not in the runbook, call any Cloudflare API or SDK, write or edit `cert.pem` / `config.yml` directly outside the runbook's instructions.
1117
+ **Does not:** drive the Cloudflare dashboard via Playwright, browser-automate master-token creation, synthesise alternative `cloudflared` flag sequences not in the runbook, write or echo any API token, write or edit `cert.pem` / `config.yml` directly outside the runbook's instructions.
1118
1118
 
1119
1119
  When a command fails, the agent reports the failure and cites the relevant recovery step. It does not improvise.
1120
1120
 
@@ -3281,7 +3281,7 @@ Grep for both in `~/.<brand>/logs/install-*.log`. Absence after a clean install
3281
3281
 
3282
3282
  The first user-domain write the agent attempts (e.g. recording who the operator is) hits the graph-write gate's `Write blocked (no-admin-user)` or `Write blocked (no-local-business)` error. The agent then asks the persona question, persists the answer through the `business-profile` skill or `profile-update.personFields`, and proceeds. The error itself is the signal — grep `Write blocked` in `~/.<brand>/logs/server.log` to confirm.
3283
3283
 
3284
- Cloudflare, WhatsApp, Telegram, and any other dormant capability surfaces on owner request via the `<dormant-plugins>` sentinel the manager injects per-spawn. Execution is the existing plugin skill (`cloudflare:setup-tunnel`, etc.) — no banner, no per-step flag.
3284
+ Cloudflare, WhatsApp, Telegram, and any other dormant capability surfaces on owner request via the `<dormant-plugins>` sentinel the manager injects per-spawn. Execution is the existing plugin skill (`cloudflare:cloudflare`, etc.) — no banner, no per-step flag.
3285
3285
 
3286
3286
  ### Per-spawn system-prompt sentinels
3287
3287
 
@@ -1,6 +1,6 @@
1
1
  # Public Agent Manager
2
2
 
3
- Create, edit, clone, list, preview, and delete public agents. Each public agent is a self-contained directory under `agents/` with its own identity, personality, knowledge, model, and selected plugins.
3
+ Create, edit, clone, list, preview, and delete public agents. Each public agent is a self-contained directory under `agents/` with its own identity, personality, knowledge, and model.
4
4
 
5
5
  ## Agent Directory Structure
6
6
 
@@ -8,13 +8,15 @@ Each agent lives at `{accountDir}/agents/{slug}/`:
8
8
 
9
9
  | File | Purpose | Required |
10
10
  |------|---------|----------|
11
- | `config.json` | Model, selected plugins, display name, status, image identity | Yes |
12
- | `IDENTITY.md` | Boundaries, behavioural rules | Yes |
13
- | `SOUL.md` | Personality only — tone, voice, warmth, greeting style | Yes |
14
- | `KNOWLEDGE.md` | Domain facts from the graph — pricing, FAQs, services | No |
11
+ | `config.json` | Model, display name, status, image identity, knowledge keywords | Yes |
12
+ | `IDENTITY.md` | Boundaries, behavioural rules — copied verbatim from the Rubytech template | Yes |
13
+ | `SOUL.md` | Personality and role — tone, voice, warmth, greeting; plus the agent's job: give basic public info and capture the visitor's contact details for human follow-up (the chat equivalent of the landing-page contact form) | Yes |
14
+ | `KNOWLEDGE.md` | The business's public-facing facts at landing-page detail — pricing, FAQs, services | Yes |
15
15
  | `KNOWLEDGE-SUMMARY.md` | Auto-generated summary when KNOWLEDGE.md exceeds context budget | No |
16
16
  | `assets/` | Per-agent images (logo, avatar, icon) | No |
17
17
 
18
+ `SOUL.md` and `KNOWLEDGE.md` are authored entirely client-side by you at agent creation and must be **non-empty before the agent goes active** — a public spawn whose SOUL or KNOWLEDGE is empty or missing is refused at spawn (the visitor sees an unavailable state, never a dud agent), so neither ships as a template nor is seeded by the installer. `IDENTITY.md` is copied verbatim from the Rubytech-controlled template, never drafted.
19
+
18
20
  ## Slug Rules
19
21
 
20
22
  - Lowercase a-z, 0-9, hyphens only
@@ -29,7 +31,6 @@ Each agent lives at `{accountDir}/agents/{slug}/`:
29
31
  "slug": "sales",
30
32
  "displayName": "Sales Agent",
31
33
  "model": "claude-haiku-4-5",
32
- "plugins": ["sales"],
33
34
  "status": "active",
34
35
  "knowledgeKeywords": [],
35
36
  "image": "/agent-assets/sales/agent-image.png",
@@ -58,7 +59,7 @@ An optional image (avatar, icon, or logo) displayed in the public chat header in
58
59
 
59
60
  **Fallback chain:** agent image → account brand logo → platform default icon. When no image is configured, the existing brand logo behaviour is preserved.
60
61
 
61
- During creation (Step 7) and editing, offer image upload when the user has an image ready. Use the `agent-image` tool with `action: "set"`, providing the file path and the appropriate shape. If the user wants the agent's name shown in the header, set `showAgentName: true` in config.json.
62
+ During creation (Step 6) and editing, offer image upload when the user has an image ready. Use the `agent-image` tool with `action: "set"`, providing the file path and the appropriate shape. If the user wants the agent's name shown in the header, set `showAgentName: true` in config.json.
62
63
 
63
64
  ### Knowledge Tagging
64
65
 
@@ -76,37 +77,15 @@ Present as a `select` field within the agent configuration `form` during creatio
76
77
  | Sonnet | `claude-sonnet-4-6` | Balanced. Nuanced conversation, qualification, support. | Standard |
77
78
  | Opus | `claude-opus-4-7` | Maximum intelligence. Complex sales, consultative selling. | Premium |
78
79
 
79
- ### Plugin Selection
80
-
81
- Present available plugins as a `multi-select` component. Selected plugin names are stored in `config.json`. The platform filters embedded plugins per-agent at runtime — only plugins whose `metadata.platform.embed` includes `"public"` are actually embedded.
82
-
83
- Only present plugins from the curated list below. Before presenting, verify each plugin's directory exists under `$PLATFORM_ROOT/plugins/` (premium plugins are only available when delivered). Omit any plugin whose directory is absent.
84
-
85
- **Core (always available):**
86
-
87
- | Plugin | Description |
88
- |--------|-------------|
89
- | `sales` | Buying signal detection, closing techniques, objection handling |
90
- | `docs` | User guide and platform documentation |
91
-
92
- **Premium — Real Agent (only when delivered):**
93
-
94
- | Plugin | Description |
95
- |--------|-------------|
96
- | `buyers` | Buyer enquiry handling and educational guides |
97
- | `estate-coaching` | Agent coaching and performance development |
98
- | `estate-sales` | Real estate sales methodology |
99
- | `estate-teaching` | Training and educational content |
100
-
101
80
  ## SOUL.md Scope Enforcement
102
81
 
103
- SOUL.md is strictly personality. When the user provides content for SOUL.md, route non-personality content to the correct file:
82
+ SOUL.md is personality and role. When the user provides content for SOUL.md, route content that belongs elsewhere to the correct file:
104
83
 
105
84
  - **Facts, pricing, services, FAQs** → KNOWLEDGE.md
106
- - **Engagement strategy, qualification flows, component usage** → handled by selected plugins
107
- - **Boundaries, tool rules, procedural instructions** → IDENTITY.md
85
+ - **Engagement and qualification how the agent draws out what the visitor needs and captures their contact details for human follow-up** → SOUL.md (this is the agent's role; the public agent is toolless, so there are no plugins to embed)
86
+ - **Boundaries and procedural rules** → IDENTITY.md (Rubytech-controlled and copied verbatim — the agent's hard limits live there, not in SOUL)
108
87
 
109
- SOUL.md answers "what does this agent feel like to talk to?" — tone, warmth, formality, humour, greeting personality. SOUL.md must not restate constraints from IDENTITY.md. When user-provided content could belong to either file, apply this test: "does this restrict what the agent does?" → IDENTITY.md. "Does this describe how the agent sounds?" → SOUL.md. If content does both (e.g. "always respond politely"), route to SOUL — the constraint is a consequence of tone, not an operational boundary. Present SOUL.md via `document-editor` for the user to review and approve. Follow the document-editor encoding constraint in IDENTITY.md when generating content.
88
+ SOUL.md answers "what does this agent feel like to talk to, and what is it here to do?" — tone, warmth, formality, greeting, and the role of giving basic public info and capturing contact details. SOUL.md must not restate constraints from IDENTITY.md. When user-provided content could belong to either file, apply this test: "does this restrict what the agent does?" → IDENTITY.md. "Does this describe how the agent sounds?" → SOUL.md. If content does both (e.g. "always respond politely"), route to SOUL — the constraint is a consequence of tone, not an operational boundary. Present SOUL.md via `document-editor` for the user to review and approve. Follow the document-editor encoding constraint in IDENTITY.md when generating content.
110
89
 
111
90
  ## KNOWLEDGE.md Population
112
91
 
@@ -136,10 +115,9 @@ The system prompt must leave room for conversation. After assembling all files,
136
115
  ```
137
116
  IDENTITY.md: 800 tokens
138
117
  SOUL.md: 400 tokens
139
- sales plugin: 2,100 tokens
140
118
  KNOWLEDGE.md: 8,200 tokens
141
119
  ─────────────────────────
142
- Total: 11,500 / 40,000 (29%)
120
+ Total: 9,400 / 40,000 (24%)
143
121
  ```
144
122
 
145
123
  Warn if total exceeds 50% of the model's context window.
@@ -148,7 +126,7 @@ Warn if total exceeds 50% of the model's context window.
148
126
 
149
127
  ### Create from Template
150
128
 
151
- When template context is provided (a path to a template directory containing `template.json`, `IDENTITY.md`, and `SOUL.md`), the Create operation pre-populates its steps from the template. The user reviews and approves every file — the template is a starting point, not an automatic deployment.
129
+ When template context is provided (a path to a template directory containing `template.json` and a `SOUL.md` seed), the Create operation pre-populates its steps from the template. The user reviews and approves every file — the template is a starting point, not an automatic deployment.
152
130
 
153
131
  Read the template's `template.json` and seed files. If `template.json` is unreadable or missing required fields (`suggestedSlug`, `displayName`), report the error and fall back to the standard Create flow.
154
132
 
@@ -156,12 +134,11 @@ The following Create steps are modified when a template is provided:
156
134
 
157
135
  - **Step 1** — use `suggestedSlug` from `template.json` as the initial slug. If an agent already exists at that slug, ask the user to choose a different name.
158
136
  - **Step 2** — skip. The agent name and role come from `template.json` `displayName` and `description`.
159
- - **Step 3** — pre-select the plugins listed in `template.json` `plugins` array. Only plugins that appear in the curated public-eligible list (see Plugin Selection above) can be pre-selected. If any template plugin is not available (not delivered, not enabled, or not marked for public embed), note which ones are missing and let the user adjust the selection.
160
- - **Step 5** — present the template's `IDENTITY.md` content via `document-editor` instead of drafting from a role description. The user can edit before approving.
161
- - **Step 6** — present the template's `SOUL.md` content via `document-editor` instead of drafting from conversation. The user can edit before approving.
162
- - **Step 7** — pre-populate form defaults: `model` from `template.json` (or `claude-haiku-4-5` if absent), `knowledgeKeywords` from `template.json` (or empty).
137
+ - **Step 4** — unchanged: `IDENTITY.md` is always the Rubytech-controlled template copied verbatim, never the agent template's own identity.
138
+ - **Step 5** — present the template's `SOUL.md` content via `document-editor` instead of drafting from conversation. The user can edit before approving.
139
+ - **Step 6** — pre-populate form defaults: `model` from `template.json` (or `claude-haiku-4-5` if absent), `knowledgeKeywords` from `template.json` (or empty).
163
140
 
164
- All other steps (4, 8-12) proceed identically — knowledge discovery, direct tagging, KNOWLEDGE.md generation, config write, budget check, and confirmation are unchanged.
141
+ All other steps (3, 7-12) proceed identically — knowledge discovery, direct tagging, KNOWLEDGE.md generation, config write, budget check, and confirmation are unchanged.
165
142
 
166
143
  After creation, no template metadata persists in the agent's files. The resulting agent is indistinguishable from one created manually. The user owns it completely.
167
144
 
@@ -169,11 +146,10 @@ After creation, no template metadata persists in the agent's files. The resultin
169
146
 
170
147
  1. **Pre-creation check** — verify the target directory (`agents/{slug}/`) does not exist or is empty before proceeding. If remnant files exist from a prior incomplete deletion, flag the issue to the user: show what files remain and offer to clean them up before starting fresh. Do not silently inherit stale files — a leftover `config.json` from a previous agent would bypass the creation gate.
171
148
  2. Ask for the agent's name (becomes the slug) and role description
172
- 3. Present available plugins (multi-select)
173
- 4. **Knowledge discovery**search the graph with admin permissions (no `agentSlug` the agent doesn't exist yet) to find available knowledge. Present available content grouped by category for the user to select.
174
- 5. Present `IDENTITY.md` via document-editor with `filePath: "agents/{slug}/IDENTITY.md"` (draft from role description). Write on approval.
175
- 6. Present `SOUL.md` via document-editor with `filePath: "agents/{slug}/SOUL.md"` (draft personality from user conversation). Write on approval.
176
- 7. Present agent configuration via a single `form` component with these fields:
149
+ 3. **Knowledge discovery** — search the graph with admin permissions (no `agentSlug` — the agent doesn't exist yet) to find available knowledge. Present available content grouped by category for the user to select.
150
+ 4. **Install `IDENTITY.md`**copy the Rubytech-controlled template verbatim to `agents/{slug}/IDENTITY.md`. It is fixed (the toolless public directive Task 615) and is never drafted, edited, or presented for review.
151
+ 5. Present `SOUL.md` via document-editor with `filePath: "agents/{slug}/SOUL.md"` (draft personality and role from user conversation — tone plus the job of giving basic public info and capturing the visitor's contact details). Write on approval. Must be non-empty; an empty SOUL is refused at spawn.
152
+ 6. Present agent configuration via a single `form` component with these fields:
177
153
  - `knowledgeKeywords` (`text`): label "Knowledge keywords (max 5, comma-separated)", placeholder with examples relevant to the agent's role
178
154
  - `model` (`select`): label "Model", options with descriptions:
179
155
  - `claude-haiku-4-5` — "Haiku — Fast, cost-effective. FAQ, routing, high-volume."
@@ -183,30 +159,30 @@ After creation, no template metadata persists in the agent's files. The resultin
183
159
  The `model` field should have a `defaultValue` of `"claude-haiku-4-5"`.
184
160
  Wait for the user's response before proceeding.
185
161
  After the form is submitted, ask the user if they have an image (avatar, icon, or logo) for the agent. If yes, ask the user to provide the file and select the shape (circle for avatars/icons, rounded for logos), then use the `agent-image` tool to upload it.
186
- 8. **Direct slug tagging (optional, gated)** — keyword subscriptions (configured in Step 7) and direct slug tagging are independent routing mechanisms. Keyword subscriptions route content by topic — any matching node is visible, now or in the future. Direct slug tagging routes specific nodes by identity — only explicitly tagged nodes are visible via the slug search path. Neither implies the other.
162
+ 7. **Direct slug tagging (optional, gated)** — keyword subscriptions (configured in Step 6) and direct slug tagging are independent routing mechanisms. Keyword subscriptions route content by topic — any matching node is visible, now or in the future. Direct slug tagging routes specific nodes by identity — only explicitly tagged nodes are visible via the slug search path. Neither implies the other.
187
163
 
188
164
  Ask the user whether any specific knowledge nodes should be tagged with this agent's slug. Explain that direct tagging makes specific nodes always visible to this agent regardless of keyword matches, and is useful for content that doesn't match any keyword but should still be available. If keyword subscriptions already cover the desired content, tagging is unnecessary.
189
165
 
190
- - If the user approves: present the knowledge discovery results from Step 4 for selection, read each node's current `agents` array first, and confirm the new slug-tag set with the user. The recorder folds the slug onto each node's `agents` array in its next turn.
166
+ - If the user approves: present the knowledge discovery results from Step 3 for selection, read each node's current `agents` array first, and confirm the new slug-tag set with the user. The recorder folds the slug onto each node's `agents` array in its next turn.
191
167
  - If the user declines: skip tagging entirely. Do not propose tagging without explicit approval.
192
- 9. **KNOWLEDGE.md generation** — populate from the now-tagged set plus keyword matches using the `update-knowledge` skill workflow
193
- 10. Write `config.json` with selected model, plugins, status "active", and `knowledgeKeywords`. This is the last gated write — placed after IDENTITY.md, SOUL.md, and KNOWLEDGE.md to prevent cascade failure if one gate stalls.
194
- 11. Check context budget — auto-summarise if over threshold
195
- 12. **Project the agent into the graph** — delegate to the `specialists:database-operator` specialist with the instruction: "Project public agent `{slug}` into the graph by POSTing to `/api/admin/agents/{slug}/project`." The route reads the on-disk files and idempotently MERGEs the `:Agent` node, the four owned `:KnowledgeDocument` projections (IDENTITY/SOUL/KNOWLEDGE/KNOWLEDGE-SUMMARY when present, namespaced `attachmentId="agent:<slug>:<role>"`), the `HAS_*` edges, and the `USES_KNOWLEDGE` edges to every operator-tagged doc. Loud-fail: if the route returns non-2xx, surface the error to the user verbatim — the agent's files exist on disk but its graph projection is incomplete, which means the operator's /graph view will not show this agent. Re-running the projection is safe; it is the same idempotent MERGE.
196
- 13. Confirm creation: "Agent created. Visitors can reach it at `/{slug}`"
168
+ 8. **KNOWLEDGE.md generation** — populate from the now-tagged set plus keyword matches using the `update-knowledge` skill workflow. KNOWLEDGE.md is required and must be non-empty; an empty or missing KNOWLEDGE is refused at spawn.
169
+ 9. Write `config.json` with selected model, status "active", and `knowledgeKeywords`. This is the last gated write — placed after IDENTITY.md, SOUL.md, and KNOWLEDGE.md to prevent cascade failure if one gate stalls.
170
+ 10. Check context budget — auto-summarise if over threshold
171
+ 11. **Project the agent into the graph** — delegate to the `specialists:database-operator` specialist with the instruction: "Project public agent `{slug}` into the graph by POSTing to `/api/admin/agents/{slug}/project`." The route reads the on-disk files and idempotently MERGEs the `:Agent` node, the four owned `:KnowledgeDocument` projections (IDENTITY/SOUL/KNOWLEDGE/KNOWLEDGE-SUMMARY when present, namespaced `attachmentId="agent:<slug>:<role>"`), the `HAS_*` edges, and the `USES_KNOWLEDGE` edges to every operator-tagged doc. Loud-fail: if the route returns non-2xx, surface the error to the user verbatim — the agent's files exist on disk but its graph projection is incomplete, which means the operator's /graph view will not show this agent. Re-running the projection is safe; it is the same idempotent MERGE.
172
+ 12. Confirm creation: "Agent created. Visitors can reach it at `/{slug}`"
197
173
 
198
174
  ### List
199
175
 
200
176
  Read `defaultAgent` from `account-manage` to identify the current default. Read all directories under `agents/`. For each, read `config.json` and estimate total token budget. Present as a structured summary, marking the default agent:
201
177
 
202
178
  **sales** — Sonnet Standard * default
203
- 1 plugin · 11,500 / 200,000 tokens (6%) · active
179
+ 11,500 / 200,000 tokens (6%) · active
204
180
 
205
181
  **support** — Haiku Low cost
206
- 0 plugins · 3,200 / 40,000 tokens (8%) · active
182
+ 3,200 / 40,000 tokens (8%) · active
207
183
 
208
184
  **public** — Haiku Low cost
209
- 1 plugin · 9,800 / 40,000 tokens (25%) · active
185
+ 9,800 / 40,000 tokens (25%) · active
210
186
 
211
187
  ### Clone
212
188
 
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cloudflare",
3
- "description": "Cloudflare Tunnel operations — setup, reset, dashboard guidance. Zero agent-facing MCP tools; every operation is the agent invoking `cloudflared` directly via Bash or quoting a dashboard click-path the operator performs themselves.",
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 minted 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 Tunnel operations — setup, reset, dashboard guidance. Zero agent-facing MCP tools; every operation is the agent invoking `cloudflared` directly via Bash or quoting a dashboard click-path the operator performs themselves.
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 minted 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. The operator signs in with OAuth via `cloudflared tunnel login` (issued by the agent through Bash); `cloudflared` writes `cert.pem` to the brand-scoped config directory. The Cloudflare dashboard is the single source of truth for which domains, addresses, and tunnels exist; the plugin never reads or mutates account state via any API path only `cloudflared` CLI shell-outs the agent runs, and DNS + HTTPS probes against public surfaces.
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 mint) 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 mint a narrowly-scoped, short-lived token per operation and exports that ephemeral token for the one call. 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,20 +25,23 @@ Each installation has its own Cloudflare account. The operator signs in with OAu
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` directly via Bash, following the numbered steps in `references/manual-setup.md`, with the cloudflared stdout/stderr streamed verbatim into the PTY chat. 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.
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 minted 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
 
32
32
  | Skill | Purpose |
33
33
  |---|---|
34
- | [setup-tunnel/SKILL.md](skills/setup-tunnel/SKILL.md) | Names the outcome contract (external HTTP 200 to the configured hostname), the inputs to collect, the runbook to execute, and the tool-discipline rule that binds the agent. |
34
+ | [cloudflare/SKILL.md](skills/cloudflare/SKILL.md) | The entry point for every Cloudflare operation. Routes each operation class to its reference, names the outcome contracts (tunnel external HTTP 200; hosting deployed URL; D1 round-trip), and states the tool-discipline + secret-redaction rules that bind the agent. |
35
35
 
36
36
  ### References
37
37
 
38
38
  | Reference | Topics |
39
39
  |---|---|
40
- | [manual-setup.md](references/manual-setup.md) | Step-by-step runbook — Steps 0–7 with isolated `cloudflared` command blocks. The agent reads the relevant step before issuing each command. |
41
- | [dashboard-guide.md](references/dashboard-guide.md) | Click-paths for the operations only the Cloudflare dashboard can perform sign in, switch accounts, add a site, edit an apex CNAME, verify nameservers, delete a tunnel, manage CNAME records. |
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, and the agent's mint-narrow-token discipline. |
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
+ | [hosting-sites.md](references/hosting-sites.md) | Deploy a static or Next.js site to Cloudflare Pages via `wrangler pages deploy`. |
44
+ | [dashboard-guide.md](references/dashboard-guide.md) | Click-paths for operations the operator prefers to do by hand — sign in, switch accounts, add a site, edit an apex CNAME, verify nameservers, delete a tunnel, manage CNAME records, author an Access policy. |
42
45
  | [reset-guide.md](references/reset-guide.md) | Decision tree for reset vs. patch, the exact `pkill` incantation for token-mode connectors, and the dashboard cleanup paths for stray records and rogue entries. |
43
46
 
44
47
  The agent loads these references on demand via `plugin-read` as the conversation requires. They are not auto-injected into the system prompt.
@@ -55,4 +58,4 @@ The setup-done claim only fires when `curl -I https://<hostname>` issued from ou
55
58
 
56
59
  ## Discipline
57
60
 
58
- Loaded into IDENTITY.md § Cloudflare operations at install time. The short form: the agent's permitted surfaces are direct `cloudflared` invocations via Bash following the runbook, the three reference files, and `curl -I` reachability checks everything else (Playwright, WebSearch-for-CF-recipes, Cloudflare API / SDK, ad-hoc `cloudflared` flag invention not in the runbook) is out of bounds. When a step fails, the agent reports the exact `cloudflared` output, cites the recovery step from `references/reset-guide.md`, and stops.
61
+ The agent's permitted surfaces are: direct `cloudflared` and `wrangler` invocations via Bash following the references; Cloudflare API calls authenticated by a freshly-minted narrow token (the master token stays in the secrets 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.
@@ -0,0 +1,166 @@
1
+ # Cloudflare API — master token, minted narrow tokens, endpoint map
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 short-lived narrow tokens from it), where the master is stored, and the curated endpoints worth knowing.
4
+
5
+ Canonical reference (always the source of truth for request shapes, never mirror it wholesale here): **https://developers.cloudflare.com/api/**
6
+
7
+ ---
8
+
9
+ ## Auth model — one master, many minted narrow tokens
10
+
11
+ There are two distinct tokens in play. Keep them separate.
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 narrow tokens. It is never passed to `wrangler`, never put on a command line that gets echoed, never printed.
14
+ - **Minted narrow token** — scoped to exactly one operation class (a single-zone DNS edit, a one-project Pages deploy, a D1 query), short-lived, created by the agent via the API call below. Exported into the environment for the one `wrangler` / API call, then discarded. Never written to disk, never committed, never echoed into chat.
15
+
16
+ Why mint instead of hand-rolling: the operator provisions the master once; every per-operation token is minted by the agent on demand, so the operator never hand-crafts scoped tokens and no broad token is ever exported to a tool that could log it.
17
+
18
+ ---
19
+
20
+ ## Master token storage — account-scoped secrets file
21
+
22
+ Store the master at an account-scoped secrets file, **not** in any deployable project tree:
23
+
24
+ ```
25
+ ~/<brand>-code/data/accounts/<accountId>/secrets/cloudflare.env # mode 600, umask 077
26
+ ```
27
+
28
+ (worked example: `~/realagent-code/data/accounts/<accountId>/secrets/cloudflare.env`)
29
+
30
+ It holds exactly two values:
31
+
32
+ ```
33
+ CLOUDFLARE_API_TOKEN=<master>
34
+ CLOUDFLARE_ACCOUNT_ID=<accountId>
35
+ ```
36
+
37
+ Rationale (binding):
38
+
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
+ 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 for the master only** — `set -a; . <file>; set +a` to load `CLOUDFLARE_API_TOKEN` + `CLOUDFLARE_ACCOUNT_ID` into the environment for the mint call; per-operation narrow tokens go into the ephemeral environment of their single command, never into this file.
42
+
43
+ Create it once (mode 600, parent dirs 700):
44
+
45
+ ```bash
46
+ ACCOUNT_ID="<accountId>"
47
+ SECRETS_DIR="${HOME}/<brand>-code/data/accounts/${ACCOUNT_ID}/secrets"
48
+ ( umask 077; mkdir -p "${SECRETS_DIR}" )
49
+ # The operator pastes the master token; the agent never echoes it back.
50
+ ( umask 077; printf 'CLOUDFLARE_API_TOKEN=%s\nCLOUDFLARE_ACCOUNT_ID=%s\n' "<master>" "${ACCOUNT_ID}" > "${SECRETS_DIR}/cloudflare.env" )
51
+ chmod 600 "${SECRETS_DIR}/cloudflare.env"
52
+ ```
53
+
54
+ Load the master for a mint call:
55
+
56
+ ```bash
57
+ set -a; . "${SECRETS_DIR}/cloudflare.env"; set +a
58
+ ```
59
+
60
+ ---
61
+
62
+ ## Provisioning the master token (ADVANCED — operator-guided, never automated)
63
+
64
+ This is the one manual, operator-driven step. The agent relays the click-path; it does **not** browser-automate it (no Playwright, no Chrome DevTools, no consent-page driver).
65
+
66
+ 1. Open **https://dash.cloudflare.com/profile/api-tokens** in your browser.
67
+ 2. Click **Create Token** → **Create Custom Token** → **Get started**.
68
+ 3. Name it (e.g. `<brand> master`). Under **Permissions**, add these rows (Account-level unless noted):
69
+ - **Account · Cloudflare Pages · Edit**
70
+ - **Account · D1 · Edit**
71
+ - **Account · API Tokens · Write** ← this is what lets the agent mint narrow tokens
72
+ - **Zone · DNS · Edit** (Zone Resources → the zones you route)
73
+ - **Account · Cloudflare Tunnel · Edit** (only if you want the API to manage tunnels alongside `cloudflared`)
74
+ 4. **Account Resources** → Include → your account. **Zone Resources** → Include → the zones in scope.
75
+ 5. Click **Continue to summary**, then **Create Token**.
76
+ 6. Copy the token **once** — Cloudflare shows it a single time. Paste it into the secrets file (above). The agent stores it without echoing it back.
77
+
78
+ Verify the master works (token value never printed — only the verification result):
79
+
80
+ ```bash
81
+ set -a; . "${SECRETS_DIR}/cloudflare.env"; set +a
82
+ curl -sS -H "Authorization: Bearer ${CLOUDFLARE_API_TOKEN}" \
83
+ "https://api.cloudflare.com/client/v4/user/tokens/verify" | jq '.result.status'
84
+ # expect: "active"
85
+ ```
86
+
87
+ ---
88
+
89
+ ## Minting a narrow token
90
+
91
+ The agent mints a per-operation token from the master via `POST /accounts/{account_id}/tokens`. Scope it to the single operation, give it a short TTL, export it for the one call, discard it.
92
+
93
+ ```bash
94
+ set -a; . "${SECRETS_DIR}/cloudflare.env"; set +a # loads the master + account id
95
+
96
+ # Example: a one-project Pages-deploy token. Adjust the policy for the operation.
97
+ MINTED=$(curl -sS -X POST \
98
+ -H "Authorization: Bearer ${CLOUDFLARE_API_TOKEN}" \
99
+ -H "Content-Type: application/json" \
100
+ "https://api.cloudflare.com/client/v4/accounts/${CLOUDFLARE_ACCOUNT_ID}/tokens" \
101
+ --data @- <<'JSON' | jq -r '.result.value'
102
+ {
103
+ "name": "ephemeral-pages-deploy",
104
+ "policies": [{
105
+ "effect": "allow",
106
+ "resources": { "com.cloudflare.api.account.<accountId>": "*" },
107
+ "permission_groups": [{ "id": "<pages-edit-permission-group-id>", "name": "Pages Write" }]
108
+ }],
109
+ "expires_on": "<iso8601-a-few-minutes-out>"
110
+ }
111
+ JSON
112
+ )
113
+
114
+ # Use it for exactly one command, in this command's environment only:
115
+ CLOUDFLARE_API_TOKEN="${MINTED}" wrangler pages deploy ./dist --project-name <project> --branch=main
116
+
117
+ unset MINTED # discard; never written to disk, never echoed
118
+ ```
119
+
120
+ 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.
121
+
122
+ **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.
123
+
124
+ ---
125
+
126
+ ## Curated endpoint map
127
+
128
+ 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 **minted narrow** token.
129
+
130
+ ### Token create / verify
131
+ - `GET /user/tokens/verify` — confirm a token is active.
132
+ - `POST /accounts/${ACC}/tokens` — mint a narrow token (see above).
133
+ - `GET /accounts/${ACC}/tokens/permission_groups` — resolve permission-group ids for scoping.
134
+
135
+ ### DNS records (zone-scoped; needs **Zone · DNS · Edit**)
136
+ - `GET /zones/${ZONE}/dns_records` — list / find a record id.
137
+ - `POST /zones/${ZONE}/dns_records` — create. For an **apex CNAME**, set `"type":"CNAME"`, `"name":"@"` (or the bare zone name), `"content":"<UUID>.cfargotunnel.com"`, `"proxied":true` — Cloudflare flattens it automatically. This is the API equivalent of the dashboard apex edit in `dashboard-guide.md`.
138
+ - `PUT/PATCH /zones/${ZONE}/dns_records/{id}` — update. `DELETE …/{id}` — remove a stray record.
139
+
140
+ ```bash
141
+ curl -sS -X POST -H "Authorization: Bearer ${TOKEN}" -H "Content-Type: application/json" \
142
+ "https://api.cloudflare.com/client/v4/zones/${ZONE}/dns_records" \
143
+ --data '{"type":"CNAME","name":"@","content":"<UUID>.cfargotunnel.com","proxied":true}' \
144
+ | jq '{ok:.success, name:.result.name}'
145
+ ```
146
+
147
+ ### Zones
148
+ - `GET /zones?account.id=${ACC}` — list zones on the account; find a zone id by name (`?name=example.com`).
149
+
150
+ ### Tunnels (Account · Cloudflare Tunnel)
151
+ - `GET /accounts/${ACC}/cfd_tunnel` — list tunnels.
152
+ - `POST /accounts/${ACC}/cfd_tunnel` — create. `DELETE …/{tunnel_id}` — delete.
153
+ - The OAuth/`cloudflared` path in `manual-setup.md` remains the primary tunnel flow; use the API only when an end-to-end programmatic run is wanted.
154
+
155
+ ### Access (Zero Trust) applications + policies
156
+ - `POST /accounts/${ACC}/access/apps` and `POST /accounts/${ACC}/access/apps/{app_id}/policies` — author an Access application + policy programmatically. The dashboard click-path in `dashboard-guide.md` § "Author an Access policy" is the alternative for operators who prefer to click.
157
+
158
+ ---
159
+
160
+ ## When to use which path
161
+
162
+ - **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.
163
+ - **Apex CNAME** → API (above) or dashboard (`dashboard-guide.md`). Either flattens correctly.
164
+ - **Pages deploy** → `wrangler` with a minted Pages-Edit token, per `hosting-sites.md`.
165
+ - **D1 read/write** → `wrangler d1 execute --remote` with a minted D1-Edit token, per `d1-data-capture.md`.
166
+ - **Inspecting account state** (which zones, which tunnels) → API `GET` with a read-scoped minted token, or the dashboard.