fix: 增加cloudflare skills并修复 Cloudflare 适配器中的分析查询限制问题
continuous-integration/drone/push Build is passing
continuous-integration/drone/push Build is passing
This commit is contained in:
1 parent
82b63c65e7
commit
400b1d2b32
365 files changed
+30636
No files matched your search
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"mcpServers": {
|
||||
"cloudflare": { "url": "https://mcp.cloudflare.com/mcp" },
|
||||
"cloudflare-docs": { "url": "https://docs.mcp.cloudflare.com/mcp" },
|
||||
"cloudflare-bindings": { "url": "https://bindings.mcp.cloudflare.com/mcp" },
|
||||
"cloudflare-builds": { "url": "https://builds.mcp.cloudflare.com/mcp" },
|
||||
"cloudflare-observability": { "url": "https://observability.mcp.cloudflare.com/mcp" }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"name": "cloudflare",
|
||||
"interface": {
|
||||
"displayName": "Cloudflare"
|
||||
},
|
||||
"plugins": [
|
||||
{
|
||||
"name": "cloudflare",
|
||||
"source": {
|
||||
"source": "url",
|
||||
"url": "https://github.com/cloudflare/skills.git"
|
||||
},
|
||||
"policy": {
|
||||
"installation": "AVAILABLE",
|
||||
"authentication": "ON_INSTALL"
|
||||
},
|
||||
"category": "Developer Tools"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"$schema": "https://code.claude.com/schemas/marketplace.json",
|
||||
"name": "cloudflare",
|
||||
"owner": {
|
||||
"name": "Cloudflare",
|
||||
"url": "https://workers.cloudflare.com"
|
||||
},
|
||||
"plugins": [
|
||||
{
|
||||
"name": "cloudflare",
|
||||
"source": "./",
|
||||
"description": "Build, test, and deploy applications on Cloudflare with platform guidance Skills and the Cloudflare MCP server."
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"name": "cloudflare",
|
||||
"description": "Build, test, and deploy applications on Cloudflare with platform guidance Skills and the Cloudflare MCP server.",
|
||||
"version": "1.0.0",
|
||||
"mcpServers": "./.mcp.json",
|
||||
"author": {
|
||||
"name": "Cloudflare"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
{
|
||||
"name": "cloudflare",
|
||||
"version": "1.0.0",
|
||||
"description": "Build, test, and deploy applications on Cloudflare with platform guidance Skills and the Cloudflare MCP server.",
|
||||
"author": {
|
||||
"name": "Cloudflare",
|
||||
"email": "support@cloudflare.com",
|
||||
"url": "https://www.cloudflare.com/products/workers/"
|
||||
},
|
||||
"homepage": "https://developers.cloudflare.com/",
|
||||
"repository": "https://github.com/cloudflare/skills",
|
||||
"license": "Apache-2.0",
|
||||
"keywords": [
|
||||
"cloudflare",
|
||||
"workers",
|
||||
"durable-objects",
|
||||
"agents",
|
||||
"mcp",
|
||||
"wrangler",
|
||||
"serverless",
|
||||
"compute",
|
||||
"storage",
|
||||
"applications"
|
||||
],
|
||||
"skills": "./skills/",
|
||||
"mcpServers": "./.mcp.json",
|
||||
"interface": {
|
||||
"displayName": "Cloudflare",
|
||||
"shortDescription": "Cloudflare platform and MCP",
|
||||
"longDescription": "Build, review, and operate on Cloudflare with guidance for Workers, Durable Objects, Agents SDK, Cloudflare One, Wrangler, web performance, Turnstile, and more, plus the Cloudflare Code Mode MCP server for live account, API, and documentation workflows.",
|
||||
"developerName": "Cloudflare",
|
||||
"category": "Developer Tools",
|
||||
"capabilities": ["Interactive", "Write"],
|
||||
"websiteURL": "https://developers.cloudflare.com/",
|
||||
"privacyPolicyURL": "https://www.cloudflare.com/privacypolicy/",
|
||||
"termsOfServiceURL": "https://www.cloudflare.com/website-terms/",
|
||||
"defaultPrompt": [
|
||||
"Build a Cloudflare Worker or agent with the right SDK and Wrangler setup",
|
||||
"Review this Cloudflare project for production Workers best practices",
|
||||
"Use Cloudflare skills and MCP to inspect or deploy this project"
|
||||
],
|
||||
"brandColor": "#F6821F",
|
||||
"composerIcon": "./logo.svg",
|
||||
"logo": "./logo.svg",
|
||||
"screenshots": []
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"name": "cloudflare",
|
||||
"owner": {
|
||||
"name": "Cloudflare"
|
||||
},
|
||||
"plugins": [
|
||||
{
|
||||
"name": "cloudflare",
|
||||
"source": "./",
|
||||
"description": "Build, test, and deploy applications on Cloudflare with platform guidance Skills and the Cloudflare MCP server."
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"name": "cloudflare",
|
||||
"version": "1.0.0",
|
||||
"description": "Build, test, and deploy applications on Cloudflare with platform guidance Skills and the Cloudflare MCP server.",
|
||||
"author": {
|
||||
"name": "Cloudflare",
|
||||
"email": "support@cloudflare.com",
|
||||
"url": "https://workers.cloudflare.com"
|
||||
},
|
||||
"keywords": [
|
||||
"cloudflare",
|
||||
"workers",
|
||||
"durable-objects",
|
||||
"agents",
|
||||
"mcp",
|
||||
"wrangler",
|
||||
"serverless",
|
||||
"compute",
|
||||
"storage",
|
||||
"applications"
|
||||
],
|
||||
"logo": "logo.svg"
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
name: Semgrep OSS scan
|
||||
on:
|
||||
pull_request: {}
|
||||
push:
|
||||
branches: [main, master]
|
||||
workflow_dispatch: {}
|
||||
schedule:
|
||||
- cron: '0 0 1-7 * 6' # per-repo, staggered across month
|
||||
concurrency:
|
||||
group: semgrep-${{ github.event_name }}-${{ github.head_ref || github.run_id }}
|
||||
cancel-in-progress: true
|
||||
permissions:
|
||||
contents: read
|
||||
jobs:
|
||||
semgrep:
|
||||
name: semgrep-oss
|
||||
runs-on: ubuntu-slim
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
with:
|
||||
fetch-depth: 1
|
||||
- id: cache-semgrep
|
||||
uses: actions/cache@v5
|
||||
with:
|
||||
path: ~/.local
|
||||
key: semgrep-1.160.0-${{ runner.os }}
|
||||
- if: steps.cache-semgrep.outputs.cache-hit != 'true'
|
||||
run: pip install --user semgrep==1.160.0
|
||||
- run: echo "$HOME/.local/bin" >> "$GITHUB_PATH"
|
||||
- run: semgrep scan --config=auto
|
||||
@@ -0,0 +1,2 @@
|
||||
pr.md
|
||||
TODO.md
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"mcpServers": {
|
||||
"cloudflare": {
|
||||
"type": "http",
|
||||
"url": "https://mcp.cloudflare.com/mcp"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
* @irvinebroque @elithrar @dmmulroy @thomasgauvin
|
||||
@@ -0,0 +1,10 @@
|
||||
# Contributing
|
||||
|
||||
Keep skills small: help agents find the right documentation instead of maintaining another copy of it.
|
||||
|
||||
For changes to skills, commands, or bundled references:
|
||||
|
||||
- Read the relevant page on [Cloudflare's developer documentation](https://developers.cloudflare.com/) and verify that it supports the proposed guidance; a working URL alone is not enough.
|
||||
- Link directly to the relevant product or workflow page. Prefer links over duplicated API signatures, limits, pricing, configuration, or examples that can become stale.
|
||||
- When correcting outdated reference content, replace it with a short pointer to the current documentation where possible.
|
||||
- If the required guidance is missing from `developers.cloudflare.com`, describe the documentation gap in the pull request rather than adding unsupported guidance to a skill.
|
||||
@@ -0,0 +1,202 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
@@ -0,0 +1,92 @@
|
||||
# Cloudflare Skills
|
||||
|
||||
A collection of [Agent Skills](https://www.anthropic.com/engineering/equipping-agents-for-the-real-world-with-agent-skills) for building on Cloudflare, Workers, the Agents SDK, and the wider Cloudflare Developer Platform.
|
||||
|
||||
## Installing
|
||||
|
||||
Use the native plugin where supported to install both Cloudflare guidance and the Cloudflare MCP server. Agents that only support the Agent Skills standard can install the skills separately.
|
||||
|
||||
### Codex
|
||||
|
||||
Install from the Cloudflare plugin marketplace:
|
||||
|
||||
```sh
|
||||
codex plugin marketplace add cloudflare/skills
|
||||
codex plugin add cloudflare@cloudflare
|
||||
```
|
||||
|
||||
Start a new Codex session after installation.
|
||||
|
||||
### Claude Code
|
||||
|
||||
Install using the [plugin marketplace](https://code.claude.com/docs/en/discover-plugins#add-from-github):
|
||||
|
||||
```
|
||||
/plugin marketplace add cloudflare/skills
|
||||
/plugin install cloudflare@cloudflare
|
||||
```
|
||||
|
||||
### VS Code / GitHub Copilot
|
||||
|
||||
Install directly from this repository:
|
||||
|
||||
1. Enable `chat.plugins.enabled` in VS Code settings.
|
||||
2. Open the Command Palette and run **Chat: Install Plugin From Source**.
|
||||
3. Enter `https://github.com/cloudflare/skills`.
|
||||
|
||||
For marketplace installation or troubleshooting, see [VS Code's agent plugin documentation](https://code.visualstudio.com/docs/agent-customization/agent-plugins).
|
||||
|
||||
### Cursor
|
||||
|
||||
Install from the Cursor Marketplace or add manually via **Settings > Rules > Add Rule > Remote Rule (Github)** with `cloudflare/skills`.
|
||||
|
||||
### npx skills
|
||||
|
||||
Install using the [`npx skills`](https://skills.sh) CLI:
|
||||
|
||||
```
|
||||
npx skills add https://github.com/cloudflare/skills
|
||||
```
|
||||
|
||||
### Clone / Copy
|
||||
|
||||
Clone this repo and copy the skill folders into the appropriate directory for your agent:
|
||||
|
||||
| Agent | Skill Directory | Docs |
|
||||
| ------------ | ---------------------------- | ---------------------------------------------------------------------------------- |
|
||||
| Claude Code | `~/.claude/skills/` | [docs](https://code.claude.com/docs/en/skills) |
|
||||
| Cursor | `~/.cursor/skills/` | [docs](https://cursor.com/docs/context/skills) |
|
||||
| OpenCode | `~/.config/opencode/skills/` | [docs](https://opencode.ai/docs/skills/) |
|
||||
| OpenAI Codex | `~/.codex/skills/` | [docs](https://developers.openai.com/codex/skills/) |
|
||||
| Pi | `~/.pi/agent/skills/` | [docs](https://github.com/badlogic/pi-mono/tree/main/packages/coding-agent#skills) |
|
||||
|
||||
## Skills
|
||||
|
||||
Skills are contextual and auto-loaded based on your conversation. When a request matches a skill's triggers, the agent loads and applies the relevant skill to provide accurate, up-to-date guidance.
|
||||
|
||||
| Skill | Useful for |
|
||||
| ------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| cloudflare | Helps agents discover which Cloudflare products fit their needs, then find the right skills and docs |
|
||||
| nextjs-on-cloudflare | Next.js on Workers with vinext; routes to vinext's upstream skills and docs |
|
||||
| agents-sdk | Building stateful AI agents with state, scheduling, RPC, MCP servers, email, and streaming chat |
|
||||
| durable-objects | Stateful coordination (chat rooms, games, booking), RPC, SQLite, alarms, WebSockets |
|
||||
| sandbox-next | Sandbox on `@cloudflare/sandbox@next` (1.0 preview); recommended for new projects |
|
||||
| sandbox-stable | Sandbox on the current stable `@cloudflare/sandbox` package |
|
||||
| sandbox-migrate-to-next | Port a stable Sandbox app to `@cloudflare/sandbox@next` |
|
||||
| wrangler | Deploying and managing Workers, KV, R2, D1, Vectorize, Queues, Workflows |
|
||||
| workers-best-practices | Writing, reviewing, or configuring production Workers |
|
||||
| cloudflare-email-service | Implementing or troubleshooting Email Sending, Email Routing, and delivery configuration |
|
||||
| turnstile-spin | Setting up, repairing, or migrating Turnstile bot verification, including server-side Siteverify |
|
||||
| web-perf | Auditing Core Web Vitals (FCP, LCP, TBT, CLS), render-blocking resources, network chains |
|
||||
| cloudflare-one | Designing, configuring, troubleshooting, or reviewing [Cloudflare One](https://developers.cloudflare.com/cloudflare-one/) deployments across Access, Gateway, WARP, Tunnel, Magic WAN, DLP, CASB, posture, and identity |
|
||||
| cloudflare-one-migrations | Migration assessments, policy mapping, rollout plans, and gap analysis for Zscaler, Palo Alto, legacy VPN/SWG, and SASE migrations to Cloudflare One |
|
||||
|
||||
## MCP Servers
|
||||
|
||||
This plugin includes Cloudflare's main [remote MCP server](https://developers.cloudflare.com/agents/model-context-protocol/cloudflare/servers-for-cloudflare/) for enhanced functionality:
|
||||
|
||||
| Server | Purpose |
|
||||
| ---------- | ---------------------------------------------------------------------------------------------- |
|
||||
| cloudflare | Access the Cloudflare API and current developer documentation through the Code Mode MCP server |
|
||||
|
||||
Cloudflare also publishes product-specific MCP servers. This plugin intentionally bundles only the main `cloudflare` server.
|
||||
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="66" height="30" viewBox="0 0 66 30" fill="none">
|
||||
<path fill="#F6821F" d="M52.688 13.028c-.22 0-.437.008-.654.015a.3.3 0 0 0-.102.024.37.37 0 0 0-.236.255l-.93 3.249c-.401 1.397-.252 2.687.422 3.634.618.876 1.646 1.39 2.894 1.45l5.045.306a.45.45 0 0 1 .435.41.5.5 0 0 1-.025.223.64.64 0 0 1-.547.426l-5.242.306c-2.848.132-5.912 2.456-6.987 5.29l-.378 1a.28.28 0 0 0 .248.382h18.054a.48.48 0 0 0 .464-.35c.32-1.153.482-2.344.48-3.54 0-7.22-5.79-13.072-12.933-13.072M44.807 29.578l.334-1.175c.402-1.397.253-2.687-.42-3.634-.62-.876-1.647-1.39-2.896-1.45l-23.665-.306a.47.47 0 0 1-.374-.199.5.5 0 0 1-.052-.434.64.64 0 0 1 .552-.426l23.886-.306c2.836-.131 5.9-2.456 6.975-5.29l1.362-3.6a.9.9 0 0 0 .04-.477C48.997 5.259 42.789 0 35.367 0c-6.842 0-12.647 4.462-14.73 10.665a6.92 6.92 0 0 0-4.911-1.374c-3.28.33-5.92 3.002-6.246 6.318a7.2 7.2 0 0 0 .18 2.472C4.3 18.241 0 22.679 0 28.133q0 .74.106 1.453a.46.46 0 0 0 .457.402h43.704a.57.57 0 0 0 .54-.418"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1008 B |
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"$schema": "https://agent-plugins.org/schemas/1.0.0/mcp.schema.json",
|
||||
"mcpServers": {
|
||||
"cloudflare": {
|
||||
"type": "streamable-http",
|
||||
"url": "https://mcp.cloudflare.com/mcp"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"$schema": "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json",
|
||||
"name": "cloudflare",
|
||||
"version": "1.0.0",
|
||||
"description": "Build, test, and deploy applications on Cloudflare with platform guidance Skills and the Cloudflare MCP server.",
|
||||
"author": {
|
||||
"name": "Cloudflare",
|
||||
"email": "support@cloudflare.com",
|
||||
"url": "https://www.cloudflare.com/products/workers/"
|
||||
},
|
||||
"homepage": "https://developers.cloudflare.com/",
|
||||
"repository": "https://github.com/cloudflare/skills",
|
||||
"license": "Apache-2.0",
|
||||
"keywords": [
|
||||
"cloudflare",
|
||||
"workers",
|
||||
"durable-objects",
|
||||
"agents",
|
||||
"mcp",
|
||||
"wrangler",
|
||||
"serverless",
|
||||
"compute",
|
||||
"storage",
|
||||
"applications"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
---
|
||||
description: Guidance for writing, reviewing, or configuring Cloudflare Workers and applications deployed to Workers.
|
||||
alwaysApply: false
|
||||
---
|
||||
|
||||
# Cloudflare Workers
|
||||
|
||||
Your knowledge of Cloudflare Workers APIs, types, and configuration may be outdated. **Prefer retrieval over pre-training.** Use the project's installed versions, generated types, and compatibility settings as the baseline. Retrieve relevant Cloudflare documentation to verify API, configuration, runtime behavior, and limit claims.
|
||||
|
||||
## Workers Defaults
|
||||
|
||||
- **Keep compatibility dates current.** Use today's date for new Workers. Encourage periodic updates for existing Workers, reviewing compatibility changes and running relevant tests.
|
||||
- **Enable logs and traces.** When creating or preparing a Worker for production, set `observability.enabled` and `observability.traces.enabled` to `true`. The top-level setting alone does not enable traces. Use structured JSON logging and configure sampling for the workload. During reviews, flag missing logs or traces. See [Workers Logs](https://developers.cloudflare.com/workers/observability/logs/workers-logs/) and [Traces](https://developers.cloudflare.com/workers/observability/traces/).
|
||||
- **Keep binding types in sync.** Run `wrangler types` after changing bindings in the project's Wrangler configuration, whether JSONC, JSON, or TOML.
|
||||
|
||||
## Shared Guidance
|
||||
|
||||
- Use [workers-best-practices](../skills/workers-best-practices/SKILL.md) for runtime patterns, anti-patterns, configuration, and platform API checks. Read the references relevant to the task.
|
||||
- Use [wrangler](../skills/wrangler/SKILL.md) for CLI commands and deployment configuration.
|
||||
- Find documentation for products used by the Worker in the [Cloudflare docs directory](https://developers.cloudflare.com/directory/). Verify limits and quotas against the affected product's documentation.
|
||||
@@ -0,0 +1,210 @@
|
||||
---
|
||||
name: agents-sdk
|
||||
description: Build, debug, or review Cloudflare Agents SDK applications using the agents package.
|
||||
---
|
||||
|
||||
# Cloudflare Agents SDK
|
||||
|
||||
Your knowledge of the Agents SDK may be outdated. **Prefer retrieval over pre-training** for any Agents SDK task.
|
||||
|
||||
## Retrieval Sources
|
||||
|
||||
Cloudflare docs: https://developers.cloudflare.com/agents/
|
||||
|
||||
| Topic | Docs URL | Use for |
|
||||
| -------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------- |
|
||||
| Getting started | [Quick start](https://developers.cloudflare.com/agents/getting-started/quick-start/) | First agent, project setup |
|
||||
| Adding to existing project | [Add to existing project](https://developers.cloudflare.com/agents/getting-started/add-to-existing-project/) | Install into existing Workers app |
|
||||
| Configuration | [Configuration](https://developers.cloudflare.com/agents/api-reference/configuration/) | `wrangler.jsonc`, bindings, assets, deployment |
|
||||
| Agent class | [Agents API](https://developers.cloudflare.com/agents/api-reference/agents-api/) | Agent lifecycle, patterns, pitfalls |
|
||||
| State | [Store and sync state](https://developers.cloudflare.com/agents/api-reference/store-and-sync-state/) | `setState`, `validateStateChange`, persistence |
|
||||
| Routing | [Routing](https://developers.cloudflare.com/agents/api-reference/routing/) | URL patterns, `routeAgentRequest` |
|
||||
| Callable methods | [Callable methods](https://developers.cloudflare.com/agents/api-reference/callable-methods/) | `@callable`, RPC, streaming, timeouts |
|
||||
| Scheduling | [Schedule tasks](https://developers.cloudflare.com/agents/api-reference/schedule-tasks/) | `schedule()`, `scheduleEvery()`, cron |
|
||||
| Workflows | [Run workflows](https://developers.cloudflare.com/agents/api-reference/run-workflows/) | `AgentWorkflow`, durable multi-step tasks |
|
||||
| HTTP/WebSockets | [WebSockets](https://developers.cloudflare.com/agents/api-reference/websockets/) | Lifecycle hooks, hibernation |
|
||||
| Chat agents | [Chat agents](https://developers.cloudflare.com/agents/communication-channels/chat/chat-agents/) | `AIChatAgent`, streaming, tools, persistence |
|
||||
| Client SDK | [Client SDK](https://developers.cloudflare.com/agents/communication-channels/chat/client-sdk/) | `useAgent`, `AgentClient`, state, RPC, HTTP |
|
||||
| Client tools | [Client tools](https://developers.cloudflare.com/agents/harnesses/think/client-tools/) | Client-side tools, `autoContinueAfterToolResult` |
|
||||
| Server-driven messages | [Autonomous responses](https://developers.cloudflare.com/agents/communication-channels/chat/autonomous-responses/) | `saveMessages`, `waitUntilStable`, server-initiated turns |
|
||||
| Resumable streaming | [Chat agents](https://developers.cloudflare.com/agents/communication-channels/chat/chat-agents/#resumable-streaming) | Stream recovery on disconnect |
|
||||
| Email | [Email](https://developers.cloudflare.com/agents/api-reference/email/) | Email routing, secure reply resolver |
|
||||
| MCP client | [MCP client](https://developers.cloudflare.com/agents/model-context-protocol/apis/client-api/) | Connecting to MCP servers |
|
||||
| MCP server | [MCP server](https://developers.cloudflare.com/agents/model-context-protocol/apis/handler-api/) | Building MCP servers with `createMcpHandler` |
|
||||
| MCP transports | [MCP transports](https://developers.cloudflare.com/agents/model-context-protocol/protocol/transport/) | Streamable HTTP, SSE, RPC transport options |
|
||||
| Securing MCP servers | [Securing MCP](https://developers.cloudflare.com/agents/model-context-protocol/guides/securing-mcp-server/) | OAuth, proxy MCP, hardening |
|
||||
| Human-in-the-loop | [Human-in-the-loop](https://developers.cloudflare.com/agents/concepts/agentic-patterns/human-in-the-loop/) | Workflow approvals, elicitation, timeout handling |
|
||||
| Durable execution | [Durable execution](https://developers.cloudflare.com/agents/api-reference/durable-execution/) | `runFiber()`, `stash()`, surviving DO eviction |
|
||||
| Queue | [Queue](https://developers.cloudflare.com/agents/api-reference/queue-tasks/) | Built-in FIFO queue, `queue()` |
|
||||
| Retries | [Retries](https://developers.cloudflare.com/agents/api-reference/retries/) | `this.retry()`, backoff/jitter |
|
||||
| Observability | [Observability](https://developers.cloudflare.com/agents/api-reference/observability/) | Diagnostics-channel events |
|
||||
| Push notifications | [Push notifications](https://developers.cloudflare.com/agents/communication-channels/webhooks/push-notifications/) | Web Push + VAPID from agents |
|
||||
| Webhooks | [Webhooks](https://developers.cloudflare.com/agents/communication-channels/webhooks/) | Receiving external webhooks |
|
||||
| Cross-domain auth | [Cross-domain auth](https://developers.cloudflare.com/agents/runtime/operations/cross-domain-authentication/) | WebSocket auth, tokens, CORS |
|
||||
| Readonly connections | [Readonly](https://developers.cloudflare.com/agents/api-reference/readonly-connections/) | `shouldConnectionBeReadonly` |
|
||||
| Voice | [Voice](https://developers.cloudflare.com/agents/api-reference/voice/) | Experimental STT/TTS, `withVoice` |
|
||||
| Browse the web | [Browser tools](https://developers.cloudflare.com/agents/api-reference/browse-the-web/) | Experimental CDP browser automation |
|
||||
| Think | [Think](https://developers.cloudflare.com/agents/api-reference/think/) | Experimental higher-level chat agent class |
|
||||
| Migrations | [AI SDK v5](https://github.com/cloudflare/agents/blob/main/docs/agents/migration-to-ai-sdk-v5.md), [AI SDK v6](https://github.com/cloudflare/agents/blob/main/docs/agents/migration-to-ai-sdk-v6.md) | Upgrading `@cloudflare/ai-chat` |
|
||||
|
||||
## Capabilities
|
||||
|
||||
The Agents SDK provides:
|
||||
|
||||
- **Persistent state** — SQLite-backed, auto-synced to clients via `setState`
|
||||
- **Callable RPC** — `@callable()` methods invoked over WebSocket
|
||||
- **Scheduling** — One-time, recurring (`scheduleEvery`), and cron tasks
|
||||
- **Workflows** — Durable multi-step background processing via `AgentWorkflow`
|
||||
- **Durable execution** — `runFiber()` / `stash()` for work that survives DO eviction
|
||||
- **Queue** — Built-in FIFO queue with retries via `queue()`
|
||||
- **Retries** — `this.retry()` with exponential backoff and jitter
|
||||
- **MCP integration** — Connect to MCP servers or build your own with `createMcpHandler`
|
||||
- **Email handling** — Receive and reply to emails with secure routing
|
||||
- **Streaming chat** — `AIChatAgent` with resumable streams, message persistence, tools
|
||||
- **Server-driven messages** — `saveMessages`, `waitUntilStable` for proactive agent turns
|
||||
- **React hooks** — `useAgent`, `useAgentChat` for client apps
|
||||
- **Observability** — `diagnostics_channel` events for state, RPC, schedule, lifecycle
|
||||
- **Push notifications** — Web Push + VAPID delivery from agents
|
||||
- **Webhooks** — Receive and verify external webhooks
|
||||
- **Voice** (experimental) — STT/TTS via `@cloudflare/voice`
|
||||
- **Browser tools** (experimental) — CDP-powered browsing via `agents/browser`
|
||||
- **Think** (experimental) — Higher-level chat agent via `@cloudflare/think`
|
||||
|
||||
## FIRST: Verify Installation
|
||||
|
||||
```bash
|
||||
npm ls agents # Should show agents package
|
||||
```
|
||||
|
||||
If not installed:
|
||||
|
||||
```bash
|
||||
npm install agents
|
||||
```
|
||||
|
||||
For chat agents:
|
||||
|
||||
```bash
|
||||
npm install agents @cloudflare/ai-chat ai @ai-sdk/react
|
||||
```
|
||||
|
||||
## Wrangler Configuration
|
||||
|
||||
```jsonc
|
||||
{
|
||||
"compatibility_flags": ["nodejs_compat"],
|
||||
"durable_objects": {
|
||||
"bindings": [{ "name": "MyAgent", "class_name": "MyAgent" }]
|
||||
},
|
||||
"migrations": [{ "tag": "v1", "new_sqlite_classes": ["MyAgent"] }]
|
||||
}
|
||||
```
|
||||
|
||||
**Gotchas:**
|
||||
|
||||
- Do NOT enable `experimentalDecorators` in tsconfig (breaks `@callable`)
|
||||
- Never edit old migrations — always add new tags
|
||||
- Each agent class needs its own DO binding + migration entry
|
||||
- Add `"ai": { "binding": "AI" }` for Workers AI
|
||||
|
||||
## Agent Class
|
||||
|
||||
```typescript
|
||||
import { Agent, routeAgentRequest, callable } from 'agents';
|
||||
|
||||
type State = { count: number };
|
||||
|
||||
export class Counter extends Agent<Env, State> {
|
||||
initialState = { count: 0 };
|
||||
|
||||
validateStateChange(nextState: State, source: Connection | 'server') {
|
||||
if (nextState.count < 0) throw new Error('Count cannot be negative');
|
||||
}
|
||||
|
||||
onStateUpdate(state: State, source: Connection | 'server') {
|
||||
console.log('State updated:', state);
|
||||
}
|
||||
|
||||
@callable()
|
||||
increment() {
|
||||
this.setState({ count: this.state.count + 1 });
|
||||
return this.state.count;
|
||||
}
|
||||
}
|
||||
|
||||
export default {
|
||||
fetch: (req, env) => routeAgentRequest(req, env) ?? new Response('Not found', { status: 404 })
|
||||
};
|
||||
```
|
||||
|
||||
## Routing
|
||||
|
||||
Requests route to `/agents/{agent-name}/{instance-name}`:
|
||||
|
||||
| Class | URL |
|
||||
| ---------- | -------------------------- |
|
||||
| `Counter` | `/agents/counter/user-123` |
|
||||
| `ChatRoom` | `/agents/chat-room/lobby` |
|
||||
|
||||
Client: `useAgent({ agent: "Counter", name: "user-123" })`
|
||||
|
||||
Custom routing: use `getAgentByName(env.MyAgent, "instance-id")` then `agent.fetch(request)`.
|
||||
|
||||
## Core APIs
|
||||
|
||||
| Task | API |
|
||||
| -------------------- | ------------------------------------------------------ |
|
||||
| Read state | `this.state.count` |
|
||||
| Write state | `this.setState({ count: 1 })` |
|
||||
| SQL query | `` this.sql`SELECT * FROM users WHERE id = ${id}` `` |
|
||||
| Schedule (delay) | `await this.schedule(60, "task", payload)` |
|
||||
| Schedule (cron) | `await this.schedule("0 * * * *", "task", payload)` |
|
||||
| Schedule (interval) | `await this.scheduleEvery(30, "poll")` |
|
||||
| RPC method | `@callable() myMethod() { ... }` |
|
||||
| Streaming RPC | `@callable({ streaming: true }) stream(res) { ... }` |
|
||||
| Start workflow | `await this.runWorkflow("ProcessingWorkflow", params)` |
|
||||
| Durable fiber | `await this.runFiber("name", async (ctx) => { ... })` |
|
||||
| Enqueue work | `this.queue("handler", payload)` |
|
||||
| Retry with backoff | `await this.retry(fn, { maxAttempts: 5 })` |
|
||||
| Broadcast to clients | `this.broadcast(message)` |
|
||||
| Get connections | `this.getConnections(tag?)` |
|
||||
|
||||
## React Client
|
||||
|
||||
Read [client-sdk.md](references/client-sdk.md) for client selection and current connection examples. For chat UI and tools, also read [streaming-chat.md](references/streaming-chat.md).
|
||||
|
||||
## References
|
||||
|
||||
### Core
|
||||
|
||||
- **[references/state-scheduling.md](references/state-scheduling.md)** — State persistence, scheduling, SQL
|
||||
- **[references/callable.md](references/callable.md)** — RPC methods, streaming, timeouts
|
||||
- **[references/routing.md](references/routing.md)** — URL patterns, custom routing, `getAgentByName`
|
||||
- **[references/configuration.md](references/configuration.md)** — Wrangler config, bindings, Vite setup
|
||||
|
||||
### Chat & Streaming
|
||||
|
||||
- **[references/streaming-chat.md](references/streaming-chat.md)** — AIChatAgent, resumable streams, tools
|
||||
- **[references/client-sdk.md](references/client-sdk.md)** — `useAgent`, `useAgentChat`, `AgentClient`
|
||||
- **[references/server-driven-messages.md](references/server-driven-messages.md)** — Trigger patterns, `saveMessages`
|
||||
- **[references/human-in-the-loop.md](references/human-in-the-loop.md)** — Approval flows, `needsApproval`
|
||||
|
||||
### Background Processing
|
||||
|
||||
- **[references/workflows.md](references/workflows.md)** — Durable Workflows integration
|
||||
- **[references/durable-execution.md](references/durable-execution.md)** — `runFiber`, `stash`, surviving eviction
|
||||
- **[references/queue-retries.md](references/queue-retries.md)** — Built-in queue, retry with backoff
|
||||
|
||||
### Integrations
|
||||
|
||||
- **[references/mcp.md](references/mcp.md)** — MCP client and server, transports, securing
|
||||
- **[references/email.md](references/email.md)** — Email routing and handling
|
||||
- **[references/webhooks-push.md](references/webhooks-push.md)** — Webhooks, push notifications
|
||||
- **[references/observability.md](references/observability.md)** — Diagnostics-channel events
|
||||
|
||||
### Experimental
|
||||
|
||||
- **[references/think.md](references/think.md)** — `@cloudflare/think` higher-level chat agent
|
||||
- **[references/voice.md](references/voice.md)** — `@cloudflare/voice` STT/TTS
|
||||
- **[references/codemode.md](references/codemode.md)** — Code Mode for tool orchestration
|
||||
- **[references/browse-the-web.md](references/browse-the-web.md)** — CDP browser tools
|
||||
@@ -0,0 +1,63 @@
|
||||
# Browse the Web (Experimental)
|
||||
|
||||
Fetch https://developers.cloudflare.com/agents/api-reference/browse-the-web/ for complete documentation.
|
||||
|
||||
CDP-powered browser tools that let agents scrape, screenshot, and interact with web pages.
|
||||
|
||||
## Setup
|
||||
|
||||
```jsonc
|
||||
// wrangler.jsonc
|
||||
{
|
||||
"browser": { "binding": "BROWSER" },
|
||||
"worker_loaders": [{ "binding": "LOADER" }],
|
||||
"compatibility_flags": ["nodejs_compat"]
|
||||
}
|
||||
```
|
||||
|
||||
## Usage with AI SDK
|
||||
|
||||
```typescript
|
||||
import { createBrowserTools } from 'agents/browser/ai';
|
||||
|
||||
export class MyAgent extends AIChatAgent<Env> {
|
||||
async onChatMessage(onFinish) {
|
||||
const browserTools = createBrowserTools({
|
||||
browser: this.env.BROWSER,
|
||||
loader: this.env.LOADER
|
||||
});
|
||||
|
||||
const result = streamText({
|
||||
model: openai('gpt-4o'),
|
||||
messages: await convertToModelMessages(this.messages),
|
||||
tools: { ...myTools, ...browserTools },
|
||||
onFinish
|
||||
});
|
||||
return result.toUIMessageStreamResponse();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Available Tools
|
||||
|
||||
| Tool | Purpose |
|
||||
| ----------------- | ------------------------------------------- |
|
||||
| `browser_search` | Search the web and return results |
|
||||
| `browser_execute` | Navigate to URL, execute JS, return results |
|
||||
|
||||
The LLM writes async JavaScript IIFEs that run in a fresh browser session.
|
||||
|
||||
## When to Use
|
||||
|
||||
- Need a real browser (JS rendering, screenshots, interaction) → browser tools
|
||||
- Just need HTML/API data → use `fetch()` instead (faster, cheaper)
|
||||
|
||||
## Low-Level API
|
||||
|
||||
```typescript
|
||||
import { connectBrowser, CdpSession } from 'agents/browser';
|
||||
|
||||
const browser = await connectBrowser(this.env.BROWSER);
|
||||
const cdp = new CdpSession(browser);
|
||||
await cdp.send('Page.navigate', { url: 'https://example.com' });
|
||||
```
|
||||
@@ -0,0 +1,92 @@
|
||||
# Callable Methods
|
||||
|
||||
Fetch https://developers.cloudflare.com/agents/api-reference/callable-methods/ for complete documentation.
|
||||
|
||||
## Overview
|
||||
|
||||
`@callable()` exposes agent methods to clients via WebSocket RPC.
|
||||
|
||||
```typescript
|
||||
import { Agent, callable } from 'agents';
|
||||
|
||||
export class MyAgent extends Agent<Env, State> {
|
||||
@callable()
|
||||
async greet(name: string): Promise<string> {
|
||||
return `Hello, ${name}!`;
|
||||
}
|
||||
|
||||
@callable()
|
||||
async processData(data: unknown): Promise<Result> {
|
||||
// Long-running work
|
||||
return result;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Client Usage
|
||||
|
||||
```typescript
|
||||
// Basic call
|
||||
const greeting = await agent.call('greet', ['World']);
|
||||
|
||||
// With timeout
|
||||
const result = await agent.call('processData', [data], {
|
||||
timeout: 5000 // 5 second timeout
|
||||
});
|
||||
```
|
||||
|
||||
## Streaming Responses
|
||||
|
||||
```typescript
|
||||
import { Agent, callable, StreamingResponse } from 'agents';
|
||||
|
||||
export class MyAgent extends Agent<Env, State> {
|
||||
@callable({ streaming: true })
|
||||
async streamResults(stream: StreamingResponse, query: string) {
|
||||
for await (const item of fetchResults(query)) {
|
||||
stream.send(JSON.stringify(item));
|
||||
}
|
||||
stream.close();
|
||||
}
|
||||
|
||||
@callable({ streaming: true })
|
||||
async streamWithError(stream: StreamingResponse) {
|
||||
try {
|
||||
// ... work
|
||||
} catch (error) {
|
||||
stream.error(error.message); // Signal error to client
|
||||
return;
|
||||
}
|
||||
stream.close();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Client with streaming:
|
||||
|
||||
```typescript
|
||||
await agent.call('streamResults', ['search term'], {
|
||||
stream: {
|
||||
onChunk: (data) => console.log('Chunk:', data),
|
||||
onDone: () => console.log('Complete'),
|
||||
onError: (error) => console.error('Error:', error)
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
## Introspection
|
||||
|
||||
```typescript
|
||||
// Get list of callable methods on an agent
|
||||
const methods = await agent.call('getCallableMethods', []);
|
||||
// Returns: ["greet", "processData", "streamResults", ...]
|
||||
```
|
||||
|
||||
## When to Use
|
||||
|
||||
| Scenario | Use |
|
||||
| ------------------------------------ | --------------------------- |
|
||||
| Browser/mobile calling agent | `@callable()` |
|
||||
| External service calling agent | `@callable()` |
|
||||
| Worker calling agent (same codebase) | DO RPC directly |
|
||||
| Agent calling another agent | `getAgentByName()` + DO RPC |
|
||||
@@ -0,0 +1,11 @@
|
||||
# Client SDK
|
||||
|
||||
Choose `useAgent` for React state/RPC, `AgentClient` for other WebSocket clients, and `agentFetch` for one-off HTTP requests. Add `useAgentChat` when the UI needs chat messages and streaming. Check installed package versions before adapting current examples.
|
||||
|
||||
| Task | Documentation |
|
||||
| ---------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| Connect, sync state, call RPC, or send HTTP requests | [Client SDK](https://developers.cloudflare.com/agents/communication-channels/chat/client-sdk/) — hooks, vanilla JS, typed calls, streaming callbacks, and connection options |
|
||||
| Build chat UI | [Chat agents](https://developers.cloudflare.com/agents/communication-channels/chat/chat-agents/) — `useAgentChat`, message rendering, status, and tool interactions |
|
||||
| Authenticate across origins | [Cross-domain authentication](https://developers.cloudflare.com/agents/runtime/operations/cross-domain-authentication/) — token validation and WebSocket authentication |
|
||||
|
||||
Keep client instance selection consistent with server routing. For authentication, account for token refresh on reconnect and query caching. Close manually created `AgentClient` connections when finished; React hooks manage their own cleanup.
|
||||
@@ -0,0 +1,110 @@
|
||||
# Codemode (Experimental)
|
||||
|
||||
Fetch https://developers.cloudflare.com/agents/api-reference/codemode/ for complete documentation.
|
||||
|
||||
Codemode lets LLMs write and execute code that orchestrates your tools, instead of calling them one at a time. The LLM gets a single "write code" tool; generated JavaScript runs in an isolated Worker sandbox.
|
||||
|
||||
## When to Use
|
||||
|
||||
| Scenario | Use Codemode? |
|
||||
| ------------------------------ | ------------------------------------- |
|
||||
| Single tool call | No — standard tool calling is simpler |
|
||||
| Chained tool calls with logic | Yes |
|
||||
| Conditional logic across tools | Yes |
|
||||
| MCP multi-server workflows | Yes |
|
||||
| Simple Q&A chat | No |
|
||||
|
||||
## Setup
|
||||
|
||||
### Wrangler Config
|
||||
|
||||
```jsonc
|
||||
{
|
||||
"worker_loaders": [{ "binding": "LOADER" }],
|
||||
"compatibility_flags": ["nodejs_compat"]
|
||||
}
|
||||
```
|
||||
|
||||
### Install
|
||||
|
||||
```bash
|
||||
npm install @cloudflare/codemode ai zod
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
```typescript
|
||||
import { createCodeTool } from '@cloudflare/codemode/ai';
|
||||
import { DynamicWorkerExecutor } from '@cloudflare/codemode';
|
||||
import { streamText, tool, convertToModelMessages } from 'ai';
|
||||
import { z } from 'zod';
|
||||
|
||||
const tools = {
|
||||
getWeather: tool({
|
||||
description: 'Get weather for a location',
|
||||
inputSchema: z.object({ location: z.string() }),
|
||||
execute: async ({ location }) => `Weather: ${location} 72°F`
|
||||
}),
|
||||
sendEmail: tool({
|
||||
description: 'Send an email',
|
||||
inputSchema: z.object({ to: z.string(), subject: z.string(), body: z.string() }),
|
||||
execute: async ({ to, subject, body }) => `Email sent to ${to}`
|
||||
})
|
||||
};
|
||||
|
||||
export class MyAgent extends Agent<Env, State> {
|
||||
async onChatMessage() {
|
||||
const executor = new DynamicWorkerExecutor({
|
||||
loader: this.env.LOADER
|
||||
});
|
||||
|
||||
const codemode = createCodeTool({ tools, executor });
|
||||
|
||||
const result = streamText({
|
||||
model,
|
||||
system: 'You are a helpful assistant.',
|
||||
messages: await convertToModelMessages(this.messages),
|
||||
tools: { codemode }
|
||||
});
|
||||
|
||||
return result.toUIMessageStreamResponse();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## With MCP Tools
|
||||
|
||||
```typescript
|
||||
const codemode = createCodeTool({
|
||||
tools: {
|
||||
...myTools,
|
||||
...this.mcp.getAITools()
|
||||
},
|
||||
executor
|
||||
});
|
||||
```
|
||||
|
||||
## How It Works
|
||||
|
||||
1. `createCodeTool` generates TypeScript type definitions from your tools
|
||||
2. The LLM writes an async arrow function calling `codemode.toolName(args)`
|
||||
3. Code runs in an isolated Worker sandbox via `DynamicWorkerExecutor`
|
||||
4. Tool calls route back to the host via Workers RPC
|
||||
5. External `fetch()` is blocked by default — sandbox can only call your tools
|
||||
|
||||
## Network Isolation
|
||||
|
||||
```typescript
|
||||
const executor = new DynamicWorkerExecutor({
|
||||
loader: env.LOADER,
|
||||
globalOutbound: null // default — fully isolated
|
||||
// globalOutbound: env.MY_SERVICE // route through a Fetcher
|
||||
});
|
||||
```
|
||||
|
||||
## Limitations
|
||||
|
||||
- Experimental — API may change
|
||||
- `needsApproval` tools execute immediately in sandbox (no approval pause yet)
|
||||
- JavaScript execution only
|
||||
- Requires `worker_loaders` binding
|
||||
@@ -0,0 +1,70 @@
|
||||
# Configuration
|
||||
|
||||
Fetch https://developers.cloudflare.com/agents/api-reference/configuration/ for complete documentation.
|
||||
|
||||
## Wrangler Config (`wrangler.jsonc`)
|
||||
|
||||
```jsonc
|
||||
{
|
||||
"name": "my-agent",
|
||||
"main": "src/index.ts",
|
||||
"compatibility_date": "2025-01-28",
|
||||
"compatibility_flags": ["nodejs_compat"],
|
||||
"durable_objects": {
|
||||
"bindings": [
|
||||
{ "name": "MyAgent", "class_name": "MyAgent" },
|
||||
{ "name": "ChatAgent", "class_name": "ChatAgent" }
|
||||
]
|
||||
},
|
||||
"migrations": [{ "tag": "v1", "new_sqlite_classes": ["MyAgent", "ChatAgent"] }],
|
||||
"ai": { "binding": "AI" },
|
||||
"assets": {
|
||||
"directory": "./dist/client",
|
||||
"binding": "ASSETS",
|
||||
"not_found_handling": "single-page-application",
|
||||
"run_worker_first": true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Key Rules
|
||||
|
||||
- Every agent class needs a DO binding AND a `new_sqlite_classes` migration entry
|
||||
- `nodejs_compat` is required
|
||||
- Never edit old migrations — add a new tag (e.g. `v2`) for new classes
|
||||
- Do NOT enable `experimentalDecorators` in tsconfig — it breaks `@callable`
|
||||
- For Workers AI locally, set `"ai": { "binding": "AI", "remote": true }` in `.dev.vars` or config
|
||||
- Use `wrangler secret put` for secrets, never hardcode them
|
||||
|
||||
## Vite Setup
|
||||
|
||||
```typescript
|
||||
import { defineConfig } from 'vite';
|
||||
import react from '@vitejs/plugin-react';
|
||||
import { cloudflare } from '@cloudflare/vite-plugin';
|
||||
import { agents } from 'agents/vite';
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [react(), cloudflare(), agents()]
|
||||
});
|
||||
```
|
||||
|
||||
## Type Generation
|
||||
|
||||
```bash
|
||||
npx wrangler types
|
||||
```
|
||||
|
||||
This generates `env.d.ts` with typed bindings. Regenerate after changing `wrangler.jsonc`.
|
||||
|
||||
## tsconfig
|
||||
|
||||
Extend the agents tsconfig for correct settings:
|
||||
|
||||
```jsonc
|
||||
{
|
||||
"extends": ["agents/tsconfig"],
|
||||
"include": ["src/**/*.ts", "src/**/*.tsx"],
|
||||
"compilerOptions": { "paths": { "~/*": ["./src/*"] } }
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,51 @@
|
||||
# Durable Execution
|
||||
|
||||
Fetch https://developers.cloudflare.com/agents/api-reference/durable-execution/ for complete documentation.
|
||||
|
||||
Fibers let agent work survive Durable Object eviction. Progress is checkpointed to SQLite; on recovery, you decide what to do.
|
||||
|
||||
## `runFiber`
|
||||
|
||||
```typescript
|
||||
export class MyAgent extends Agent<Env, State> {
|
||||
async onRequest(request: Request) {
|
||||
await this.runFiber('process-data', async (ctx) => {
|
||||
const step1 = await fetchData();
|
||||
ctx.stash({ step: 1, data: step1 });
|
||||
|
||||
const step2 = await transform(step1);
|
||||
ctx.stash({ step: 2, result: step2 });
|
||||
|
||||
this.setState({ result: step2 });
|
||||
});
|
||||
return new Response('Started');
|
||||
}
|
||||
|
||||
async onFiberRecovered(ctx) {
|
||||
const checkpoint = ctx.stash;
|
||||
if (checkpoint.step === 1) {
|
||||
const step2 = await transform(checkpoint.data);
|
||||
this.setState({ result: step2 });
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Key APIs
|
||||
|
||||
| API | Purpose |
|
||||
| -------------------------- | ------------------------------------------- |
|
||||
| `this.runFiber(name, fn)` | Start a named fiber |
|
||||
| `ctx.stash` / `this.stash` | Read latest checkpoint |
|
||||
| `ctx.stash = data` | Write checkpoint (JSON-serializable) |
|
||||
| `onFiberRecovered(ctx)` | Called on DO restart if fiber was in-flight |
|
||||
| `keepAlive()` | Prevent hibernation while fiber runs |
|
||||
| `keepAliveWhile(fn)` | Keep alive for duration of async function |
|
||||
|
||||
## Important
|
||||
|
||||
- `stash` replaces the entire checkpoint — not a merge
|
||||
- The lambda is NOT restored on recovery — only the stash data is. You must re-derive what to do in `onFiberRecovered`
|
||||
- No auto-retry on throw — handle errors yourself
|
||||
- For long-running pipelines with automatic retries, use Workflows instead
|
||||
- Filter concurrent fibers by `ctx.name` in `onFiberRecovered`
|
||||
@@ -0,0 +1,144 @@
|
||||
# Email Handling
|
||||
|
||||
Fetch https://developers.cloudflare.com/agents/api-reference/email/ for complete documentation.
|
||||
|
||||
## Overview
|
||||
|
||||
Agents receive and reply to emails via Cloudflare Email Routing.
|
||||
|
||||
## Wrangler Configuration
|
||||
|
||||
```jsonc
|
||||
{
|
||||
"durable_objects": {
|
||||
"bindings": [{ "name": "EmailAgent", "class_name": "EmailAgent" }]
|
||||
},
|
||||
"migrations": [{ "tag": "v1", "new_sqlite_classes": ["EmailAgent"] }],
|
||||
"send_email": [{ "name": "SEB", "destination_address": "reply@yourdomain.com" }]
|
||||
}
|
||||
```
|
||||
|
||||
## Basic Email Handler
|
||||
|
||||
```typescript
|
||||
import { Agent } from 'agents';
|
||||
import { type AgentEmail } from 'agents/email';
|
||||
import PostalMime from 'postal-mime';
|
||||
|
||||
export class EmailAgent extends Agent<Env, State> {
|
||||
async onEmail(email: AgentEmail) {
|
||||
const raw = await email.getRaw();
|
||||
const parsed = await PostalMime.parse(raw);
|
||||
|
||||
console.log('From:', email.from);
|
||||
console.log('Subject:', parsed.subject);
|
||||
|
||||
await this.replyToEmail(email, {
|
||||
fromName: 'My Agent',
|
||||
subject: `Re: ${parsed.subject}`,
|
||||
body: 'Thanks for your email!'
|
||||
});
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Routing Emails
|
||||
|
||||
```typescript
|
||||
import { routeAgentRequest, routeAgentEmail } from 'agents';
|
||||
import { createAddressBasedEmailResolver } from 'agents/email';
|
||||
|
||||
export default {
|
||||
async email(message, env) {
|
||||
await routeAgentEmail(message, env, {
|
||||
resolver: createAddressBasedEmailResolver('EmailAgent')
|
||||
});
|
||||
},
|
||||
|
||||
async fetch(request, env) {
|
||||
return routeAgentRequest(request, env) ?? new Response('Not found', { status: 404 });
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
## Resolvers
|
||||
|
||||
### Address-Based (Inbound Mail)
|
||||
|
||||
Routes based on recipient address:
|
||||
|
||||
```typescript
|
||||
import { createAddressBasedEmailResolver } from 'agents/email';
|
||||
|
||||
const resolver = createAddressBasedEmailResolver('EmailAgent');
|
||||
// support@example.com → EmailAgent, instance "support"
|
||||
// NotificationAgent+user123@example.com → NotificationAgent, instance "user123"
|
||||
```
|
||||
|
||||
### Secure Reply (Reply Flows)
|
||||
|
||||
Verifies replies are authentic using HMAC-SHA256 signatures:
|
||||
|
||||
```typescript
|
||||
import { createSecureReplyEmailResolver } from 'agents/email';
|
||||
|
||||
const resolver = createSecureReplyEmailResolver(env.EMAIL_SECRET, {
|
||||
maxAge: 7 * 24 * 60 * 60, // 7 days (default: 30 days)
|
||||
onInvalidSignature: (email, reason) => {
|
||||
console.warn(`Invalid signature from ${email.from}: ${reason}`);
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
Sign outbound emails to enable secure reply routing:
|
||||
|
||||
```typescript
|
||||
await this.replyToEmail(email, {
|
||||
fromName: 'My Agent',
|
||||
body: 'Thanks!',
|
||||
secret: this.env.EMAIL_SECRET // Signs headers for secure reply routing
|
||||
});
|
||||
```
|
||||
|
||||
### Catch-All (Single Instance)
|
||||
|
||||
Routes all emails to one agent instance:
|
||||
|
||||
```typescript
|
||||
import { createCatchAllEmailResolver } from 'agents/email';
|
||||
|
||||
const resolver = createCatchAllEmailResolver('EmailAgent', 'default');
|
||||
```
|
||||
|
||||
### Combining Resolvers
|
||||
|
||||
```typescript
|
||||
async email(message, env) {
|
||||
const secureReply = createSecureReplyEmailResolver(env.EMAIL_SECRET);
|
||||
const addressBased = createAddressBasedEmailResolver("EmailAgent");
|
||||
|
||||
await routeAgentEmail(message, env, {
|
||||
resolver: async (email, env) => {
|
||||
// Try secure reply first
|
||||
const result = await secureReply(email, env);
|
||||
if (result) return result;
|
||||
// Fall back to address-based
|
||||
return addressBased(email, env);
|
||||
}
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
## Utilities
|
||||
|
||||
```typescript
|
||||
import { isAutoReplyEmail } from "agents/email";
|
||||
|
||||
async onEmail(email: AgentEmail) {
|
||||
if (isAutoReplyEmail(email.headers)) {
|
||||
// Skip auto-replies (vacation, out-of-office, etc.)
|
||||
return;
|
||||
}
|
||||
// Process email...
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,10 @@
|
||||
# Human-in-the-Loop
|
||||
|
||||
Choose the approval layer based on where execution must pause, then fetch its current documentation:
|
||||
|
||||
| Need | Documentation |
|
||||
| ------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| Approve chat tool execution or run a browser-side tool | [Chat agents](https://developers.cloudflare.com/agents/communication-channels/chat/chat-agents/) — `needsApproval`, approval responses, client tools, and custom denial messages |
|
||||
| Pause a durable background task or collect MCP input | [Human-in-the-loop patterns](https://developers.cloudflare.com/agents/concepts/agentic-patterns/human-in-the-loop/) — workflow approval, timeout handling, and elicitation |
|
||||
|
||||
Distinguish approval responses from client tool outputs. When returning a custom tool error, check whether an explicit continuation is needed. Handle workflow approval timeouts before executing the gated action. Check installed SDK versions before adapting examples.
|
||||
@@ -0,0 +1,13 @@
|
||||
# MCP Integration
|
||||
|
||||
For new servers, prefer `createMcpHandler` over the deprecated `McpAgent`. For existing servers, check the installed SDK version and state/session requirements before choosing a migration path.
|
||||
|
||||
Read the relevant current documentation for implementation details and supported dependency versions:
|
||||
|
||||
| Task | Documentation |
|
||||
| -------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| Build a server | [Handler API](https://developers.cloudflare.com/agents/model-context-protocol/apis/handler-api/) — server factories, Worker entrypoint, dependencies, and examples |
|
||||
| Migrate an existing server | [MCP SDK v2 migration](https://developers.cloudflare.com/agents/model-context-protocol/guides/migrate-to-mcp-sdk-v2/) — stateless migration and temporary legacy paths |
|
||||
| Connect to servers and use their tools | [Client API](https://developers.cloudflare.com/agents/model-context-protocol/apis/client-api/) — connections, OAuth, tools, resources, and retries |
|
||||
| Choose a transport | [Transports](https://developers.cloudflare.com/agents/model-context-protocol/protocol/transport/) — remote HTTP and existing RPC integrations |
|
||||
| Secure a server | [Securing MCP servers](https://developers.cloudflare.com/agents/model-context-protocol/guides/securing-mcp-server/) — OAuth and proxy security |
|
||||
@@ -0,0 +1,44 @@
|
||||
# Observability
|
||||
|
||||
Fetch https://developers.cloudflare.com/agents/api-reference/observability/ for complete documentation.
|
||||
|
||||
Agents emit structured events via Node.js `diagnostics_channel`. Subscribe in development or forward via Tail Workers in production.
|
||||
|
||||
## Subscribe to Events
|
||||
|
||||
```typescript
|
||||
import { subscribe } from 'agents/observability';
|
||||
|
||||
subscribe('agents:rpc', (event) => {
|
||||
console.log(`RPC call: ${event.payload.method}`);
|
||||
});
|
||||
|
||||
subscribe('agents:state', (event) => {
|
||||
console.log(`State change on ${event.agent}`);
|
||||
});
|
||||
```
|
||||
|
||||
## Available Channels
|
||||
|
||||
| Channel | Events |
|
||||
| ------------------ | ------------------------------------- |
|
||||
| `agents:state` | State changes |
|
||||
| `agents:rpc` | `@callable` invocations |
|
||||
| `agents:message` | WebSocket messages |
|
||||
| `agents:schedule` | Schedule triggers |
|
||||
| `agents:lifecycle` | Agent start, connect, disconnect |
|
||||
| `agents:workflow` | Workflow progress, completion, errors |
|
||||
| `agents:mcp` | MCP server connections, tool calls |
|
||||
| `agents:email` | Email received |
|
||||
|
||||
## Per-Agent Override
|
||||
|
||||
```typescript
|
||||
export class MyAgent extends Agent<Env, State> {
|
||||
observability = undefined; // disable for this agent
|
||||
}
|
||||
```
|
||||
|
||||
## Production: Tail Workers
|
||||
|
||||
In production, events appear as `diagnosticsChannelEvents` on the Tail Worker `event` object. Attach a Tail Worker to your agent's Worker to forward events to your observability platform.
|
||||
@@ -0,0 +1,18 @@
|
||||
# Queue & Retries
|
||||
|
||||
Read the current Cloudflare documentation for queue management, retry options, defaults, and callback examples.
|
||||
|
||||
| Task | Documentation |
|
||||
| --------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------- |
|
||||
| Enqueue, inspect, and remove background work; understand sequential processing and failure handling | [Queue tasks](https://developers.cloudflare.com/agents/runtime/execution/queue-tasks/) |
|
||||
| Retry an operation or configure retries for queued and scheduled callbacks | [Retries](https://developers.cloudflare.com/agents/runtime/execution/retries/) |
|
||||
| Delay recovery or run work on a recurring schedule | [Schedule tasks](https://developers.cloudflare.com/agents/runtime/execution/schedule-tasks/) |
|
||||
|
||||
Keep these execution choices in mind when using the linked guides:
|
||||
|
||||
- Use the built-in queue for sequential background work. Retries block later queue items; use scheduling for long recovery waits.
|
||||
- Retry delays keep the Durable Object active. Choose retry budgets with execution cost and latency in mind.
|
||||
- Queued items are removed after their retry budget is exhausted; there is no built-in dead-letter queue. Record failures explicitly when the application needs recovery or auditing.
|
||||
- The selective retry predicate is available on `this.retry()`, not serialized queue or schedule options. Handle non-retryable errors in those callbacks.
|
||||
|
||||
See [state-scheduling.md](state-scheduling.md) for choosing schedule modes and persisting application state.
|
||||
@@ -0,0 +1,78 @@
|
||||
# Routing
|
||||
|
||||
Fetch https://developers.cloudflare.com/agents/api-reference/routing/ for complete documentation.
|
||||
|
||||
## Default URL Pattern
|
||||
|
||||
`/agents/{kebab-class-name}/{instance-name}`
|
||||
|
||||
```typescript
|
||||
import { routeAgentRequest } from 'agents';
|
||||
|
||||
export default {
|
||||
fetch: (req, env) => routeAgentRequest(req, env) ?? new Response('Not found', { status: 404 })
|
||||
};
|
||||
```
|
||||
|
||||
| Class | URL |
|
||||
| ---------- | -------------------------- |
|
||||
| `Counter` | `/agents/counter/user-123` |
|
||||
| `ChatRoom` | `/agents/chat-room/lobby` |
|
||||
| `MyAgent` | `/agents/my-agent/default` |
|
||||
|
||||
Subpaths after the instance name (e.g. `/agents/my-agent/default/api/data`) route to `onRequest`.
|
||||
|
||||
## Custom Routing with `getAgentByName`
|
||||
|
||||
```typescript
|
||||
import { getAgentByName } from 'agents';
|
||||
|
||||
export default {
|
||||
async fetch(req, env) {
|
||||
const url = new URL(req.url);
|
||||
if (url.pathname.startsWith('/api/')) {
|
||||
const agent = getAgentByName(env.MyAgent, 'singleton');
|
||||
return agent.fetch(req);
|
||||
}
|
||||
return routeAgentRequest(req, env);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
## Options
|
||||
|
||||
```typescript
|
||||
routeAgentRequest(req, env, {
|
||||
cors: true,
|
||||
prefix: '/api/agents',
|
||||
locationHint: 'enam',
|
||||
jurisdiction: 'eu',
|
||||
props: { userId: '123' },
|
||||
onBeforeConnect: async (req) => {
|
||||
/* auth check */
|
||||
},
|
||||
onBeforeRequest: async (req) => {
|
||||
/* auth check */
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
`props` are delivered to `onStart(props)` on first access.
|
||||
|
||||
## Client Side
|
||||
|
||||
```tsx
|
||||
useAgent({
|
||||
agent: 'MyAgent',
|
||||
name: 'instance-1',
|
||||
host: 'https://my-worker.workers.dev',
|
||||
basePath: '/api/agents',
|
||||
path: '/custom-subpath'
|
||||
});
|
||||
```
|
||||
|
||||
## Common Mistakes
|
||||
|
||||
- Class name `MyAgent` becomes kebab `my-agent` in URLs — match exactly
|
||||
- "Namespace not found" error = the `class_name` in wrangler doesn't match your exported class
|
||||
- If `sendIdentityOnConnect: false`, the `ready` promise on the client may never resolve — use state sync instead
|
||||
@@ -0,0 +1,7 @@
|
||||
# Server-Driven Messages
|
||||
|
||||
Read [Autonomous responses](https://developers.cloudflare.com/agents/communication-channels/chat/autonomous-responses/) for scheduled, webhook, email, and agent-triggered turns, message schemas, response hooks, and client streaming status.
|
||||
|
||||
Choose `saveMessages` to persist messages and request a model response, or `persistMessages` to update context without starting a turn. Use `onChatResponse` to react to turns regardless of their trigger. For webhooks that need a quick acknowledgement, consult the documented `submitMessages` path.
|
||||
|
||||
Before reading conversation history or calling `saveMessages` from non-chat entry points, await `waitUntilStable` and handle a timeout without proceeding as if the conversation were stable. Prefer the functional `saveMessages` form when calls can queue, so each update uses the latest history.
|
||||
@@ -0,0 +1,15 @@
|
||||
# State & Scheduling
|
||||
|
||||
Read the current Cloudflare documentation before implementing state, SQL, or scheduling; check installed SDK versions when adapting an existing agent.
|
||||
|
||||
| Task | Documentation |
|
||||
| --------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------- |
|
||||
| Define state, validate updates, choose state versus SQL, and query SQLite | [Store and sync state](https://developers.cloudflare.com/agents/runtime/lifecycle/state/) |
|
||||
| Synchronize state with React or vanilla JavaScript clients | [Client SDK](https://developers.cloudflare.com/agents/communication-channels/chat/client-sdk/) |
|
||||
| Select delayed, date-based, cron, or interval execution; manage schedules and callbacks | [Schedule tasks](https://developers.cloudflare.com/agents/runtime/execution/schedule-tasks/) |
|
||||
| Configure schedule retry behavior | [Retries](https://developers.cloudflare.com/agents/runtime/execution/retries/) |
|
||||
| Handle lifecycle events, connections, and hibernation | [WebSockets](https://developers.cloudflare.com/agents/runtime/communication/websockets/) |
|
||||
|
||||
Use synchronized state for data clients need immediately, and SQL for larger collections, history, or queries. Reject invalid updates in the validation hook; state-change notifications are for reacting to accepted updates. Use the state documentation for current hook names and behavior.
|
||||
|
||||
Choose a one-time delay or date for work that runs once, cron for calendar recurrence, and an interval for a fixed cadence. For queued work and retry tradeoffs, see [queue-retries.md](queue-retries.md).
|
||||
@@ -0,0 +1,16 @@
|
||||
# Streaming Chat with AIChatAgent
|
||||
|
||||
Use `AIChatAgent` for persisted conversations with streaming and tools; use callable streaming RPC for non-chat output. Before adapting an existing app, check its installed `agents`, `@cloudflare/ai-chat`, and AI SDK versions against the current docs.
|
||||
|
||||
Read the relevant documentation before implementing:
|
||||
|
||||
| Task | Documentation |
|
||||
| --------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| Build a chat agent | [Chat agent example](https://developers.cloudflare.com/agents/examples/chat-agent/) — setup, provider, server, and UI |
|
||||
| Implement or customize chat | [Chat agents](https://developers.cloudflare.com/agents/communication-channels/chat/chat-agents/) — message format, tools, custom streams, persistence, concurrency, cancellation, and recovery |
|
||||
| Connect a client | [Client guidance](client-sdk.md) — React, vanilla JS, HTTP, and authentication |
|
||||
| Stream non-chat results | [Callable methods](https://developers.cloudflare.com/agents/runtime/lifecycle/callable-methods/) — server and client streaming RPC |
|
||||
| Trigger background turns | [Server-driven messages](server-driven-messages.md) |
|
||||
| Add approvals | [Human-in-the-loop](human-in-the-loop.md) |
|
||||
|
||||
Forward the request abort signal to the model call so cancellation stops generation. When customizing streams, verify persistence and completion behavior for the installed version. Treat client reconnection and Durable Object eviction as separate recovery cases.
|
||||
@@ -0,0 +1,112 @@
|
||||
# Think (Experimental)
|
||||
|
||||
Fetch https://developers.cloudflare.com/agents/api-reference/think/ for complete documentation.
|
||||
|
||||
`@cloudflare/think` — a higher-level chat agent class that handles the `streamText` loop, tool execution, and message persistence for you. You provide `getModel()` and `getSystemPrompt()`; Think handles the rest.
|
||||
|
||||
```bash
|
||||
npm install @cloudflare/think
|
||||
```
|
||||
|
||||
## Minimal Agent
|
||||
|
||||
```typescript
|
||||
import { Think } from '@cloudflare/think';
|
||||
import { createWorkersAI } from 'workers-ai-provider';
|
||||
import { routeAgentRequest } from 'agents';
|
||||
|
||||
export class MyAgent extends Think<Env> {
|
||||
getModel() {
|
||||
return createWorkersAI({ binding: this.env.AI })('@cf/meta/llama-4-scout-17b-16e-instruct');
|
||||
}
|
||||
|
||||
getSystemPrompt() {
|
||||
return 'You are a helpful assistant.';
|
||||
}
|
||||
}
|
||||
|
||||
export default {
|
||||
fetch: (req, env) => routeAgentRequest(req, env)
|
||||
};
|
||||
```
|
||||
|
||||
## Wrangler Config
|
||||
|
||||
```jsonc
|
||||
{
|
||||
"compatibility_flags": ["nodejs_compat", "experimental"],
|
||||
"durable_objects": {
|
||||
"bindings": [{ "name": "MyAgent", "class_name": "MyAgent" }]
|
||||
},
|
||||
"migrations": [{ "tag": "v1", "new_sqlite_classes": ["MyAgent"] }],
|
||||
"ai": { "binding": "AI" }
|
||||
}
|
||||
```
|
||||
|
||||
**Note:** Think requires the `experimental` compatibility flag.
|
||||
|
||||
## Custom Tools
|
||||
|
||||
```typescript
|
||||
import { tool } from 'ai';
|
||||
import { z } from 'zod';
|
||||
|
||||
export class MyAgent extends Think<Env> {
|
||||
getTools() {
|
||||
return {
|
||||
getWeather: tool({
|
||||
description: 'Get weather',
|
||||
parameters: z.object({ city: z.string() }),
|
||||
execute: async ({ city }) => `72°F in ${city}`
|
||||
})
|
||||
};
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Lifecycle Hooks
|
||||
|
||||
| Hook | When | Use for |
|
||||
| ------------------------ | ------------------------ | ------------------------------------------------------- |
|
||||
| `configureSession()` | Agent starts | Set up memory, context providers |
|
||||
| `beforeTurn(ctx)` | Before each LLM call | Per-turn model/tools/system prompt; return `TurnConfig` |
|
||||
| `onChunk(chunk)` | Each streaming chunk | Progress tracking |
|
||||
| `onChatResponse(result)` | After LLM turn completes | Chaining, follow-up `saveMessages` |
|
||||
| `onChatError(error)` | On LLM error | Error handling |
|
||||
|
||||
```typescript
|
||||
async beforeTurn(ctx: TurnContext): Promise<TurnConfig> {
|
||||
if (ctx.continuation) {
|
||||
return { model: cheaperModel };
|
||||
}
|
||||
return {};
|
||||
}
|
||||
```
|
||||
|
||||
## Sub-Agents
|
||||
|
||||
```typescript
|
||||
const child = this.subAgent(SpecialistAgent, 'specialist-1');
|
||||
await child.chat('Analyze this data...', (chunk) => {
|
||||
// stream callback
|
||||
});
|
||||
```
|
||||
|
||||
## Client
|
||||
|
||||
Same React hooks as `AIChatAgent`:
|
||||
|
||||
```tsx
|
||||
const agent = useAgent({ agent: 'MyAgent', name: 'session-1' });
|
||||
const { messages, input, handleInputChange, handleSubmit } = useAgentChat({ agent });
|
||||
```
|
||||
|
||||
## Think vs AIChatAgent
|
||||
|
||||
| | Think | AIChatAgent |
|
||||
| ------------------ | --------------------------- | ------------------------------- |
|
||||
| `streamText` loop | Built-in | You write it |
|
||||
| Tool execution | Automatic | You wire it |
|
||||
| Customization | Override hooks | Full control in `onChatMessage` |
|
||||
| Built-in tools | Workspace, execute, browser | None |
|
||||
| Compatibility flag | Requires `experimental` | Standard |
|
||||
@@ -0,0 +1,70 @@
|
||||
# Voice (Experimental)
|
||||
|
||||
Fetch https://developers.cloudflare.com/agents/api-reference/voice/ for complete documentation.
|
||||
|
||||
`@cloudflare/voice` — real-time speech-to-text and text-to-speech for agents. Audio streams over WebSocket.
|
||||
|
||||
```bash
|
||||
npm install @cloudflare/voice
|
||||
```
|
||||
|
||||
## Server
|
||||
|
||||
```typescript
|
||||
import { Agent } from 'agents';
|
||||
import { withVoice, WorkersAITTS, WorkersAINova3STT } from '@cloudflare/voice';
|
||||
|
||||
export class VoiceAgent extends withVoice(Agent)<Env> {
|
||||
transcriber = new WorkersAINova3STT(this);
|
||||
tts = new WorkersAITTS(this);
|
||||
|
||||
async onTurn(transcript: string, context: VoiceTurnContext) {
|
||||
const result = streamText({
|
||||
model: createWorkersAI({ binding: this.env.AI })('@cf/meta/llama-4-scout-17b-16e-instruct'),
|
||||
messages: [
|
||||
{ role: 'system', content: 'You are a voice assistant.' },
|
||||
...context.conversationHistory,
|
||||
{ role: 'user', content: transcript }
|
||||
]
|
||||
});
|
||||
|
||||
for await (const chunk of result.textStream) {
|
||||
if (context.signal.aborted) break;
|
||||
context.speak(chunk);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Lifecycle Hooks
|
||||
|
||||
| Hook | Purpose |
|
||||
| ------------------------- | ------------------------------------ |
|
||||
| `onTurn(transcript, ctx)` | Handle transcribed speech (required) |
|
||||
| `beforeCallStart(conn)` | Auth/validation before call starts |
|
||||
| `onCallStart(conn)` | Call connected |
|
||||
| `onCallEnd(conn)` | Call disconnected |
|
||||
| `onInterrupt()` | User interrupted agent speech |
|
||||
|
||||
## Client (React)
|
||||
|
||||
```tsx
|
||||
import { useVoiceAgent } from '@cloudflare/voice/react';
|
||||
|
||||
function VoiceUI() {
|
||||
const { isConnected, isSpeaking, connect, disconnect } = useVoiceAgent({
|
||||
agent: 'VoiceAgent',
|
||||
name: 'session-1'
|
||||
});
|
||||
|
||||
return (
|
||||
<button onClick={isConnected ? disconnect : connect}>
|
||||
{isConnected ? 'End Call' : 'Start Call'}
|
||||
</button>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## STT/TTS Providers
|
||||
|
||||
Workers AI (default), Deepgram, ElevenLabs — install the provider package and swap the `transcriber`/`tts` properties.
|
||||
@@ -0,0 +1,90 @@
|
||||
# Webhooks & Push Notifications
|
||||
|
||||
## Webhooks
|
||||
|
||||
Fetch https://developers.cloudflare.com/agents/communication-channels/webhooks/ for complete documentation.
|
||||
|
||||
Route external webhooks to agent instances via `onRequest`:
|
||||
|
||||
```typescript
|
||||
export default {
|
||||
async fetch(req: Request, env: Env) {
|
||||
const url = new URL(req.url);
|
||||
if (url.pathname.startsWith('/webhooks/')) {
|
||||
const entityId = url.pathname.split('/')[2];
|
||||
const agent = getAgentByName(env.MyAgent, entityId);
|
||||
return agent.fetch(req);
|
||||
}
|
||||
return routeAgentRequest(req, env);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
In the agent:
|
||||
|
||||
```typescript
|
||||
export class MyAgent extends Agent<Env, State> {
|
||||
async onRequest(request: Request) {
|
||||
const signature = request.headers.get('X-Signature');
|
||||
if (!verifySignature(signature, await request.text(), this.env.WEBHOOK_SECRET)) {
|
||||
return new Response('Unauthorized', { status: 401 });
|
||||
}
|
||||
const payload = JSON.parse(await request.text());
|
||||
this.queue('processWebhook', payload);
|
||||
return new Response('OK', { status: 202 });
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Tips:** Respond quickly (200/202), verify signatures, deduplicate with stored event IDs, use `queue()` for async processing.
|
||||
|
||||
## Push Notifications
|
||||
|
||||
Fetch https://developers.cloudflare.com/agents/communication-channels/webhooks/push-notifications/ for complete documentation.
|
||||
|
||||
Web Push via VAPID from agents. Store subscriptions in agent state, send via `web-push`.
|
||||
|
||||
```bash
|
||||
npm install web-push
|
||||
```
|
||||
|
||||
```typescript
|
||||
import webpush from 'web-push';
|
||||
|
||||
export class NotifyAgent extends Agent<Env, State> {
|
||||
@callable()
|
||||
async subscribe(subscription: PushSubscription) {
|
||||
this.setState({
|
||||
...this.state,
|
||||
subscriptions: [...this.state.subscriptions, subscription]
|
||||
});
|
||||
}
|
||||
|
||||
async sendReminder(payload: { message: string }, schedule: Schedule) {
|
||||
for (const sub of this.state.subscriptions) {
|
||||
try {
|
||||
await webpush.sendNotification(
|
||||
sub,
|
||||
JSON.stringify({
|
||||
title: 'Reminder',
|
||||
body: payload.message
|
||||
}),
|
||||
{
|
||||
vapidDetails: {
|
||||
subject: 'mailto:you@example.com',
|
||||
publicKey: this.env.VAPID_PUBLIC_KEY,
|
||||
privateKey: this.env.VAPID_PRIVATE_KEY
|
||||
}
|
||||
}
|
||||
);
|
||||
} catch (err) {
|
||||
if (err.statusCode === 404 || err.statusCode === 410) {
|
||||
// Remove expired subscription
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
VAPID keys: generate with `npx web-push generate-vapid-keys`, store as secrets.
|
||||
@@ -0,0 +1,21 @@
|
||||
# Workflows Integration
|
||||
|
||||
Use Agents for interactive communication and state management. Add a Workflow when a task needs durable multi-step execution, independent retries, or waits for external approval. Choose based on recovery needs; consult [Run Workflows](https://developers.cloudflare.com/agents/runtime/execution/run-workflows/) before implementing the integration.
|
||||
|
||||
## Read for the task
|
||||
|
||||
| Task | Documentation |
|
||||
| -------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| Define a typed `AgentWorkflow`, start it from an Agent, and configure bindings | [Quick start](https://developers.cloudflare.com/agents/runtime/execution/run-workflows/#quick-start) |
|
||||
| Call back into the originating Agent and understand durable versus non-durable helpers | [AgentWorkflow class](https://developers.cloudflare.com/agents/runtime/execution/run-workflows/#agentworkflow-class) |
|
||||
| Send events, query instances, pause, resume, terminate, or delete tracked workflows | [Agent workflow methods](https://developers.cloudflare.com/agents/runtime/execution/run-workflows/#agent-workflow-methods) |
|
||||
| Receive progress, completion, errors, and custom events | [Lifecycle callbacks](https://developers.cloudflare.com/agents/runtime/execution/run-workflows/#lifecycle-callbacks) |
|
||||
| Approve or reject a waiting task | [Human-in-the-loop approval](https://developers.cloudflare.com/agents/runtime/execution/run-workflows/#human-in-the-loop-approval) |
|
||||
| Persist Workflow results into Agent state | [State synchronization](https://developers.cloudflare.com/agents/runtime/execution/run-workflows/#state-synchronization) |
|
||||
| Define steps, parameters, retries, and returned values | [Workers API](https://developers.cloudflare.com/workflows/build/workers-api/) |
|
||||
|
||||
## Design checks
|
||||
|
||||
Keep external side effects within durable steps, make retried operations idempotent, and persist the values needed after recovery through step results. Use the [Rules of Workflows](https://developers.cloudflare.com/workflows/build/rules-of-workflows/) to choose step boundaries.
|
||||
|
||||
Progress reports and client broadcasts may repeat on retry. Use the documented durable step helpers for persistent Agent state changes and completion reporting; see [bidirectional communication](https://developers.cloudflare.com/agents/runtime/execution/run-workflows/#bidirectional-communication).
|
||||
@@ -0,0 +1,73 @@
|
||||
---
|
||||
name: cloudflare-email-service
|
||||
description: Implement or troubleshoot Cloudflare Email Sending and Email Routing integrations and their delivery configuration.
|
||||
---
|
||||
|
||||
# Cloudflare Email Service
|
||||
|
||||
Your knowledge of the Cloudflare Email Service, Email Routing or Email Sending may be outdated. **Prefer retrieval over pre-training** for any Cloudflare Email Service task.
|
||||
|
||||
Cloudflare Email Service lets you send transactional emails and route incoming emails, all within the Cloudflare platform. Your knowledge of this product may be outdated — it launched in 2025 and is evolving rapidly. **Prefer retrieval over pre-training** for any Email Service task.
|
||||
|
||||
**If there is any discrepancy between this skill and the sources below, always trust the original source.** The Cloudflare docs, REST API spec, `@cloudflare/workers-types`, and Agents SDK repo are the source of truth. This skill is a convenience guide — it may lag behind the latest changes. When in doubt, retrieve from the sources below and use what they say.
|
||||
|
||||
## Retrieval Sources
|
||||
|
||||
| Source | How to retrieve | Use for |
|
||||
| --------------- | ----------------------------------------------------------------------------------------- | ----------------------------------------------- |
|
||||
| Cloudflare docs | Cloudflare MCP `docs` tool or URL `https://developers.cloudflare.com/email-service/` | API reference, limits, pricing, latest features |
|
||||
| REST API spec | `https://developers.cloudflare.com/api/resources/email_sending` | OpenAPI spec for the Email Sending REST API |
|
||||
| Workers types | `https://www.npmjs.com/package/@cloudflare/workers-types` | Type signatures, binding shapes |
|
||||
| Agents SDK docs | [Email agent walkthrough](https://developers.cloudflare.com/agents/examples/email-agent/) | Email handling in Agents SDK |
|
||||
|
||||
## FIRST: Check Prerequisites
|
||||
|
||||
Before writing any email code, verify the basics are in place:
|
||||
|
||||
1. **Domain onboarded?** Run `npx wrangler email sending list` to see which domains have email sending enabled. If the domain isn't listed, run `npx wrangler email sending enable userdomain.com` or see [cli-and-mcp.md](references/cli-and-mcp.md) for full setup instructions.
|
||||
2. **Binding configured?** Look for `send_email` in `wrangler.jsonc` (for Workers)
|
||||
3. **postal-mime installed?** Run `npm ls postal-mime` (only needed for receiving/parsing emails)
|
||||
|
||||
## What Do You Need?
|
||||
|
||||
Start here. Find your situation, then follow the link for full details.
|
||||
|
||||
| I want to... | Path | Reference |
|
||||
| -------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------- | ------------------------------------------------- |
|
||||
| **Send emails from a Cloudflare Worker** | Workers binding (no API keys needed) | [sending.md](references/sending.md) |
|
||||
| **Send emails from an AI agent built with [Cloudflare Agents SDK](https://developers.cloudflare.com/agents/)** | `onEmail()` + `replyToEmail()` in Agent class | [sending.md](references/sending.md) |
|
||||
| **Send emails from an external app or agent** (Node.js, Go, Python, etc.) | REST API with Bearer token | [rest-api.md](references/rest-api.md) |
|
||||
| **Send emails from a coding agent** (Claude Code, Cursor, Copilot, etc.) | MCP tools, wrangler CLI, or REST API | [cli-and-mcp.md](references/cli-and-mcp.md) |
|
||||
| **Receive and process incoming emails** (Email Routing) | Workers `email()` handler | [routing.md](references/routing.md) |
|
||||
| **Set up Email Sending or Email Routing** | `wrangler email sending enable` / `wrangler email routing enable`, or Dashboard | [cli-and-mcp.md](references/cli-and-mcp.md) |
|
||||
| **Improve deliverability, avoid spam folders** | Authentication, content, compliance | [deliverability.md](references/deliverability.md) |
|
||||
|
||||
## Sending Workflow
|
||||
|
||||
Prefer the binding for Workers; use REST for external apps or when explicitly requested. Read [sending.md](references/sending.md) or [rest-api.md](references/rest-api.md) to retrieve the documentation for the selected task before writing code. These guides cover setup, recipients, attachments, headers, limits, response handling, and errors; the Workers guide also covers Agents SDK integration and types matched to the project configuration.
|
||||
|
||||
## Common Mistakes
|
||||
|
||||
| Mistake | Why It Happens | Fix |
|
||||
| -------------------------------------------------- | --------------------------------------------------------------- | --------------------------------------------------------------------------------------------------- |
|
||||
| Forgetting `send_email` binding in wrangler config | Email Service uses a binding, not an API key | Add `"send_email": [{ "name": "EMAIL" }]` to wrangler.jsonc |
|
||||
| Sending from an unverified domain | Domain must be onboarded onto Email Sending before first send | Run `wrangler email sending enable yourdomain.com` or onboard in Dashboard |
|
||||
| Reading `message.raw` twice in email handler | The raw stream is single-use — second read returns empty | Buffer first: `const raw = await new Response(message.raw).arrayBuffer()` |
|
||||
| Missing `text` field (HTML only) | Some email clients only show plain text; also helps spam scores | Always include both `html` and `text` versions |
|
||||
| Using email for marketing/bulk sends | Email Service is for transactional email only | Use a dedicated marketing email platform for newsletters and campaigns |
|
||||
| Forwarding to unverified destinations | `message.forward()` only works with verified addresses | Run `wrangler email routing addresses create user@gmail.com` or add in Dashboard |
|
||||
| Testing with fake addresses | Bounces from non-existent addresses hurt sender reputation | Use real addresses you control during development |
|
||||
| Hardcoding API tokens in source code | Tokens in code get committed and leaked | Use environment variables or Cloudflare secrets |
|
||||
| Ignoring the `from` domain requirement | The `from` address must use a domain onboarded to Email Service | Verify the domain first, then send from `anything@that-domain.com` |
|
||||
| Using `email` key in REST API `from` object | REST API uses `address` not `email` for `from` object | Use `{ "address": "...", "name": "..." }` for REST, `{ "email": "...", "name": "..." }` for Workers |
|
||||
| Using `replyTo` in REST API | REST API uses snake_case field names | Use `reply_to` for REST API, `replyTo` for Workers binding |
|
||||
|
||||
## References
|
||||
|
||||
Read the reference that matches your situation. You don't need all of them.
|
||||
|
||||
- **[references/sending.md](references/sending.md)** — Documentation map for Workers binding, attachments, and Agents SDK email.
|
||||
- **[references/rest-api.md](references/rest-api.md)** — Documentation map for HTTP sending, request schemas, responses, and errors.
|
||||
- **[references/routing.md](references/routing.md)** — Inbound `email()` handler, forwarding, replying, parsing. For receiving emails.
|
||||
- **[references/cli-and-mcp.md](references/cli-and-mcp.md)** — Domain setup, wrangler commands, MCP tools. For first-time setup.
|
||||
- **[references/deliverability.md](references/deliverability.md)** — SPF/DKIM/DMARC, bounces, suppressions, best practices.
|
||||
@@ -0,0 +1,125 @@
|
||||
# CLI, MCP, and Project Setup
|
||||
|
||||
Manage Cloudflare Email Service from the command line and coding agents.
|
||||
|
||||
For full CLI reference, run `npx wrangler email --help`. For Dashboard setup, see the [getting started docs](https://developers.cloudflare.com/email-service/get-started/).
|
||||
|
||||
## Wrangler Email Commands
|
||||
|
||||
```
|
||||
wrangler email routing
|
||||
├── enable/disable <domain> # Toggle email routing
|
||||
├── dns get <domain> # Show required DNS records
|
||||
├── rules list/create/update/delete # Manage routing rules
|
||||
└── addresses list/create/delete # Destination addresses (account-scoped)
|
||||
|
||||
wrangler email sending
|
||||
├── enable/disable <domain> # Toggle email sending
|
||||
├── dns get <domain> # Show sending DNS records (SPF, DKIM)
|
||||
├── send --from --to ... # Send an email (builder flags)
|
||||
└── send-raw --from --to ... # Send a raw MIME email
|
||||
```
|
||||
|
||||
## Domain Setup
|
||||
|
||||
### Via Dashboard
|
||||
|
||||
1. Navigate to **Compute & AI** > **Email Service** > **Email Sending** (or **Email Routing**)
|
||||
2. Select **Onboard Domain** > choose domain > **Add records and onboard**
|
||||
|
||||
This auto-adds SPF (TXT) and DKIM (CNAME/TXT) records. DNS usually propagates within 5-15 minutes.
|
||||
|
||||
### Via CLI
|
||||
|
||||
```bash
|
||||
npx wrangler email sending enable yourdomain.com
|
||||
npx wrangler email sending dns get yourdomain.com # Verify records
|
||||
```
|
||||
|
||||
## Local Development
|
||||
|
||||
Add `"remote": true` to send real emails during `wrangler dev`:
|
||||
|
||||
```jsonc
|
||||
{ "send_email": [{ "name": "EMAIL", "remote": true }] }
|
||||
```
|
||||
|
||||
```bash
|
||||
npx wrangler dev
|
||||
```
|
||||
|
||||
Emails are actually sent — use test addresses you control. Remove `"remote": true` before deploying.
|
||||
|
||||
## Cloudflare MCP Server
|
||||
|
||||
If you have the [Cloudflare MCP server](https://github.com/cloudflare/mcp) (`https://mcp.cloudflare.com/mcp`) configured, you can manage Email Service through its `search` and `execute` tools.
|
||||
|
||||
Use `search` to find email sending endpoints:
|
||||
|
||||
```javascript
|
||||
// search tool — find all email sending API endpoints
|
||||
async () => {
|
||||
const results = [];
|
||||
for (const [path, methods] of Object.entries(spec.paths)) {
|
||||
if (path.includes('email/sending')) {
|
||||
for (const [method, op] of Object.entries(methods)) {
|
||||
results.push({ method: method.toUpperCase(), path, summary: op.summary });
|
||||
}
|
||||
}
|
||||
}
|
||||
return results;
|
||||
};
|
||||
```
|
||||
|
||||
Then use `execute` to call them — for example, checking sending limits or sending an email:
|
||||
|
||||
```javascript
|
||||
// execute tool — check sending quota
|
||||
async () => {
|
||||
return cloudflare.request({
|
||||
method: 'GET',
|
||||
path: `/accounts/${accountId}/email/sending/limits`
|
||||
});
|
||||
};
|
||||
|
||||
// execute tool — send an email
|
||||
async () => {
|
||||
return cloudflare.request({
|
||||
method: 'POST',
|
||||
path: `/accounts/${accountId}/email/sending/send`,
|
||||
body: {
|
||||
to: 'user@example.com',
|
||||
from: { address: 'notifications@yourdomain.com', name: 'My App' },
|
||||
subject: 'Deployment Complete',
|
||||
html: '<h1>Deployed!</h1>',
|
||||
text: 'Deployed!'
|
||||
}
|
||||
});
|
||||
};
|
||||
```
|
||||
|
||||
GraphQL analytics queries also work through `execute` — see [deliverability.md](deliverability.md#graphql-analytics-api) for query examples. Note that email analytics are **zone-level** datasets (`emailSendingAdaptiveGroups`, `emailSendingAdaptive`) queried under `viewer > zones`, and require the **Analytics Read** token permission.
|
||||
|
||||
## Sending from CLI / Agents
|
||||
|
||||
```bash
|
||||
npx wrangler email sending send \
|
||||
--from "agent@yourdomain.com" \
|
||||
--to "developer@company.com" \
|
||||
--subject "Deployment Complete" \
|
||||
--text "Your Worker was deployed successfully."
|
||||
```
|
||||
|
||||
Or via REST API:
|
||||
|
||||
```bash
|
||||
curl "https://api.cloudflare.com/client/v4/accounts/${CLOUDFLARE_ACCOUNT_ID}/email/sending/send" \
|
||||
--header "Authorization: Bearer ${CLOUDFLARE_API_TOKEN}" \
|
||||
--header "Content-Type: application/json" \
|
||||
--data '{
|
||||
"to": "developer@company.com",
|
||||
"from": {"address": "agent@yourdomain.com", "name": "Build Agent"},
|
||||
"subject": "Deployment Complete",
|
||||
"text": "Your Worker was deployed successfully."
|
||||
}'
|
||||
```
|
||||
+276
@@ -0,0 +1,276 @@
|
||||
# Email Deliverability & Best Practices
|
||||
|
||||
For full details, see the [deliverability docs](https://developers.cloudflare.com/email-service/concepts/deliverability/) and [email authentication docs](https://developers.cloudflare.com/email-service/concepts/email-authentication/). All the monitoring endpoints below can be called via the [REST API](rest-api.md), [Wrangler CLI, or the Cloudflare MCP server](cli-and-mcp.md).
|
||||
|
||||
## What Cloudflare Handles
|
||||
|
||||
When you onboard a domain, Cloudflare auto-configures:
|
||||
|
||||
- **SPF** — TXT records authorizing Cloudflare's sending infrastructure
|
||||
- **DKIM** — Records for cryptographic signing of outbound emails
|
||||
- **IP reputation** — Managed sending infrastructure optimized for deliverability
|
||||
- **Soft bounce retries** — Automatic exponential backoff for temporary failures
|
||||
- **Suppression lists** — Hard-bounced addresses automatically blocked
|
||||
- **Feedback loops** — ISP complaint signals processed and acted on
|
||||
|
||||
Consider adding a **DMARC** record if you don't have one: `v=DMARC1; p=quarantine; rua=mailto:dmarc-reports@yourdomain.com`
|
||||
|
||||
## Bounce Handling
|
||||
|
||||
**Hard bounces** — permanent failures (address doesn't exist, domain doesn't exist). Never retried. Address auto-added to suppression list. Sending to suppressed address returns `E_RECIPIENT_SUPPRESSED`.
|
||||
|
||||
**Soft bounces** — temporary failures (mailbox full, server down, greylisting). Cloudflare auto-retries with exponential backoff.
|
||||
|
||||
## Suppression Lists
|
||||
|
||||
**Account list** (your account) — spam complaints from recipients. Cloudflare integrates with Postmasters to auto-suppress. You can manually add/remove addresses in the Dashboard.
|
||||
|
||||
See the [suppressions docs](https://developers.cloudflare.com/email-service/concepts/suppressions/) for details.
|
||||
|
||||
## Your Responsibilities
|
||||
|
||||
### Content
|
||||
|
||||
- Include both HTML and plain text versions
|
||||
- Use a recognizable sender name: `{ email: "noreply@app.com", name: "My App" }`
|
||||
- Write honest subject lines — avoid ALL CAPS, excessive punctuation
|
||||
- Include `List-Unsubscribe` headers for recurring emails
|
||||
- Use full URLs from your domain — avoid URL shorteners
|
||||
|
||||
### List Quality
|
||||
|
||||
- Validate email addresses before sending
|
||||
- Implement double opt-in for subscriptions
|
||||
- Honor unsubscribe requests promptly
|
||||
|
||||
### Transactional Only
|
||||
|
||||
Email Service is for **transactional email** (triggered by user actions: signups, password resets, order confirmations). Marketing/bulk campaigns are not permitted — use a dedicated marketing platform.
|
||||
|
||||
## Monitoring Deliverability
|
||||
|
||||
### Dashboard
|
||||
|
||||
Per-domain and account-wide analytics are available in the Cloudflare dashboard:
|
||||
|
||||
1. Log in to the [Cloudflare dashboard](https://dash.cloudflare.com) and select your account.
|
||||
2. Go to **Compute & AI** > **Email Service**.
|
||||
3. Select a domain or view account-wide metrics.
|
||||
4. Select the **Analytics** tab.
|
||||
|
||||
### Send Response
|
||||
|
||||
Every send (REST API or Workers binding) returns immediate delivery feedback. Check the response to track per-send outcomes:
|
||||
|
||||
```json
|
||||
{
|
||||
"result": {
|
||||
"delivered": ["user@example.com"],
|
||||
"permanent_bounces": ["bad@nonexistent.com"],
|
||||
"queued": ["slow@recipient.com"]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Log these to build your own delivery rate metrics.
|
||||
|
||||
### Sending Limits
|
||||
|
||||
Check your account's daily sending quota:
|
||||
|
||||
```bash
|
||||
curl "https://api.cloudflare.com/client/v4/accounts/{account_id}/email/sending/limits" \
|
||||
--header "Authorization: Bearer <API_TOKEN>"
|
||||
```
|
||||
|
||||
Returns:
|
||||
|
||||
```json
|
||||
{
|
||||
"result": {
|
||||
"quota": { "value": 5000, "unit": "day" }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Suppression List
|
||||
|
||||
Addresses that hard-bounced or received spam complaints are auto-suppressed. You can query and manage suppressions via the API.
|
||||
|
||||
**List suppressions (account-wide):**
|
||||
|
||||
```bash
|
||||
curl "https://api.cloudflare.com/client/v4/accounts/{account_id}/email/sending/suppression?page=1&per_page=100&order=created_at&direction=desc" \
|
||||
--header "Authorization: Bearer <API_TOKEN>"
|
||||
```
|
||||
|
||||
Returns:
|
||||
|
||||
```json
|
||||
{
|
||||
"page": 1,
|
||||
"per_page": 100,
|
||||
"total": 2,
|
||||
"result": [
|
||||
{
|
||||
"id": "396a5436-d4b0-42a6-b3fc-48e8fa522321",
|
||||
"email": "bounced@example.com",
|
||||
"reason": "hard_bounce",
|
||||
"created_at": "2026-03-15T10:00:00Z",
|
||||
"expires_at": null
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Query params: `page`, `per_page` (max 1000), `order` (`email` | `expires_at` | `created_at`), `direction` (`asc` | `desc`).
|
||||
|
||||
**Manually suppress an address:**
|
||||
|
||||
```bash
|
||||
curl -X POST "https://api.cloudflare.com/client/v4/accounts/{account_id}/email/sending/suppression" \
|
||||
--header "Authorization: Bearer <API_TOKEN>" \
|
||||
--header "Content-Type: application/json" \
|
||||
--data '{ "email": "user@example.com", "expires_at": "2026-06-01T00:00:00Z" }'
|
||||
```
|
||||
|
||||
`expires_at` is optional — omit for permanent suppression.
|
||||
|
||||
**Remove a suppression:**
|
||||
|
||||
```bash
|
||||
curl -X DELETE "https://api.cloudflare.com/client/v4/accounts/{account_id}/email/sending/suppression/{suppression_id}" \
|
||||
--header "Authorization: Bearer <API_TOKEN>"
|
||||
```
|
||||
|
||||
Zone-level suppressions are also available at `/zones/{zone_id}/email/sending/suppression` with the same interface.
|
||||
|
||||
### GraphQL Analytics API
|
||||
|
||||
Email Service exposes two zone-level datasets via the [GraphQL Analytics API](https://developers.cloudflare.com/analytics/graphql-api/). You can explore the schema interactively at [graphql.cloudflare.com/explorer](https://graphql.cloudflare.com/explorer). Metrics are retained for 31 days.
|
||||
|
||||
| Dataset | Description |
|
||||
| ---------------------------- | ------------------------------------------------------------------------------------- |
|
||||
| `emailSendingAdaptiveGroups` | Aggregated counts grouped by dimensions (status, date, domain, auth results, etc.) |
|
||||
| `emailSendingAdaptive` | Individual email events with full detail (from, to, subject, messageId, errors, etc.) |
|
||||
|
||||
These are **zone-level** datasets — query under `viewer > zones`, not `accounts`.
|
||||
|
||||
**Aggregated dimensions** (`emailSendingAdaptiveGroups`):
|
||||
|
||||
| Dimension | Type | Description |
|
||||
| -------------------------------- | ------ | --------------------------------------------------------------------------------------------------------- |
|
||||
| `date` | Date | Day-level grouping |
|
||||
| `datetime` | Time | Exact timestamp (also: `datetimeMinute`, `datetimeFiveMinutes`, `datetimeFifteenMinutes`, `datetimeHour`) |
|
||||
| `status` | string | Delivery status |
|
||||
| `eventType` | string | Event type |
|
||||
| `sendingDomain` | string | The sending domain |
|
||||
| `envelopeTo` | string | Recipient address |
|
||||
| `errorCause` | string | Error cause for failed sends |
|
||||
| `arc`, `dkim`, `dmarc`, `spf` | string | Email authentication results |
|
||||
| `isSpam`, `isNDR`, `isLastEvent` | uint8 | Boolean flags |
|
||||
| `spamScore`, `spamThreshold` | uint32 | Spam scoring |
|
||||
|
||||
**Individual event fields** (`emailSendingAdaptive`) additionally include: `from`, `to`, `subject`, `messageId`, `sessionId`, `errorDetail`.
|
||||
|
||||
**Email counts by status and date:**
|
||||
|
||||
```graphql
|
||||
query EmailSendingByStatus($zoneTag: string!, $start: Date!, $end: Date!) {
|
||||
viewer {
|
||||
zones(filter: { zoneTag: $zoneTag }) {
|
||||
emailSendingAdaptiveGroups(
|
||||
filter: { date_geq: $start, date_leq: $end }
|
||||
limit: 10000
|
||||
orderBy: [date_DESC]
|
||||
) {
|
||||
count
|
||||
dimensions {
|
||||
date
|
||||
status
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Filter by status (e.g. only failures):**
|
||||
|
||||
```graphql
|
||||
query EmailFailures($zoneTag: string!, $start: Date!, $end: Date!) {
|
||||
viewer {
|
||||
zones(filter: { zoneTag: $zoneTag }) {
|
||||
emailSendingAdaptiveGroups(
|
||||
filter: { date_geq: $start, date_leq: $end, status: "deliveryFailed" }
|
||||
limit: 10000
|
||||
orderBy: [date_DESC]
|
||||
) {
|
||||
count
|
||||
dimensions {
|
||||
date
|
||||
errorCause
|
||||
sendingDomain
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Individual email events (troubleshooting):**
|
||||
|
||||
```graphql
|
||||
query RecentEmailEvents($zoneTag: string!, $start: Time!, $end: Time!) {
|
||||
viewer {
|
||||
zones(filter: { zoneTag: $zoneTag }) {
|
||||
emailSendingAdaptive(
|
||||
filter: { datetime_geq: $start, datetime_leq: $end }
|
||||
limit: 50
|
||||
orderBy: [datetime_DESC]
|
||||
) {
|
||||
datetime
|
||||
from
|
||||
to
|
||||
subject
|
||||
status
|
||||
eventType
|
||||
sendingDomain
|
||||
messageId
|
||||
errorCause
|
||||
errorDetail
|
||||
dkim
|
||||
dmarc
|
||||
spf
|
||||
isSpam
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Note: `emailSendingAdaptive` filters use `datetime_geq`/`datetime_leq` (Time type, e.g. `"2026-04-01T00:00:00Z"`), while `emailSendingAdaptiveGroups` uses `date_geq`/`date_leq` (Date type, e.g. `"2026-04-01"`).
|
||||
|
||||
**curl example:**
|
||||
|
||||
```bash
|
||||
curl "https://api.cloudflare.com/client/v4/graphql" \
|
||||
--header "Authorization: Bearer <API_TOKEN>" \
|
||||
--header "Content-Type: application/json" \
|
||||
--data '{
|
||||
"query": "query($zoneTag:string!,$start:Date!,$end:Date!){viewer{zones(filter:{zoneTag:$zoneTag}){emailSendingAdaptiveGroups(filter:{date_geq:$start,date_leq:$end},limit:10000,orderBy:[date_DESC]){count,dimensions{date,status}}}}}",
|
||||
"variables": {
|
||||
"zoneTag": "<ZONE_ID>",
|
||||
"start": "2026-03-15",
|
||||
"end": "2026-04-15"
|
||||
}
|
||||
}'
|
||||
```
|
||||
|
||||
## Metrics to Watch
|
||||
|
||||
| Metric | Target | If Out of Range |
|
||||
| ---------------- | ------ | ----------------------------------------------- |
|
||||
| Delivery rate | > 95% | Check for invalid addresses; verify DNS records |
|
||||
| Hard bounce rate | < 2% | Clean your email list |
|
||||
| Complaint rate | < 0.1% | Make unsubscribe easier; stop unwanted emails |
|
||||
@@ -0,0 +1,17 @@
|
||||
# Sending Emails — REST API
|
||||
|
||||
Use the REST API for HTTP integrations from external applications, or when the user explicitly requests it inside a Worker. Otherwise prefer the [Workers binding](sending.md).
|
||||
|
||||
Read the relevant page before building the request. Keep credentials in the project's existing secret or environment-variable mechanism, and inspect the installed client SDK version if one is used.
|
||||
|
||||
| Task | Read |
|
||||
| ---------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| Authenticate, send an email, or find the complete request schema | [REST API](https://developers.cloudflare.com/email-service/api/send-emails/rest-api/) and its linked Email Sending API reference |
|
||||
| Set multiple recipients, CC/BCC, or named addresses | [Specify recipients](https://developers.cloudflare.com/email-service/examples/email-sending/recipients/) (REST examples) |
|
||||
| Encode attachments | [REST attachments](https://developers.cloudflare.com/email-service/api/send-emails/rest-api/#attachments) |
|
||||
| Add custom headers | [Email headers](https://developers.cloudflare.com/email-service/reference/headers/) |
|
||||
| Check recipient, message-size, or sending quotas | [Limits](https://developers.cloudflare.com/email-service/platform/limits/) |
|
||||
| Interpret delivery outcomes | [REST response](https://developers.cloudflare.com/email-service/api/send-emails/rest-api/#response) |
|
||||
| Diagnose errors and decide whether to retry | [REST error handling](https://developers.cloudflare.com/email-service/api/send-emails/rest-api/#error-handling) |
|
||||
|
||||
Do not reuse a Workers binding payload or response parser unchanged: verify field names, attachment encoding, response shape, and error handling for the chosen API. Validate both successful responses and failures through the project's existing checks; distinguish retryable service failures from requests that need correction.
|
||||
@@ -0,0 +1,213 @@
|
||||
# Receiving & Routing Inbound Email
|
||||
|
||||
Handle incoming emails sent to your domain via a Worker's `email()` handler. Forward, reply, reject, or parse emails programmatically.
|
||||
|
||||
For full API details, see the [Email Routing docs](https://developers.cloudflare.com/email-service/api/route-emails/email-handler/).
|
||||
|
||||
## Email Handler
|
||||
|
||||
Export an `email()` function from your Worker. No special wrangler binding needed — a routing rule connects incoming addresses to your Worker.
|
||||
|
||||
```typescript
|
||||
export default {
|
||||
async email(message, env, ctx): Promise<void> {
|
||||
console.log(`Email from ${message.from} to ${message.to}`);
|
||||
await message.forward('team@company.com');
|
||||
}
|
||||
} satisfies ExportedHandler<Env>;
|
||||
```
|
||||
|
||||
Set up routing rules in **Dashboard** > **Compute & AI** > **Email Service** > **Email Routing** > **Routing Rules**, or via `wrangler email routing rules create`.
|
||||
|
||||
## ForwardableEmailMessage
|
||||
|
||||
The `message` parameter is a `ForwardableEmailMessage`. Run `npx wrangler types` to get the full type definition. Key properties and methods:
|
||||
|
||||
- `message.from` / `message.to` — envelope addresses (SMTP MAIL FROM / RCPT TO). `message.from` is trustworthy; header addresses can be spoofed.
|
||||
- `message.headers` — `Headers` object (use `.get("subject")`, `.get("message-id")`, etc.)
|
||||
- `message.raw` — `ReadableStream<Uint8Array>` of raw MIME content. **Single use** — buffer before accessing.
|
||||
- `message.rawSize` — size in bytes
|
||||
- `message.setReject(reason)` — reject with a permanent SMTP error
|
||||
- `message.forward(rcptTo, headers?)` — forward to a verified destination
|
||||
- `message.reply(emailMessage)` — reply with an `EmailMessage` object
|
||||
|
||||
## Core Operations
|
||||
|
||||
### Forward
|
||||
|
||||
```typescript
|
||||
await message.forward('team@company.com');
|
||||
|
||||
// With custom headers
|
||||
await message.forward(
|
||||
'team@company.com',
|
||||
new Headers({
|
||||
'X-Original-Recipient': message.to
|
||||
})
|
||||
);
|
||||
```
|
||||
|
||||
Destination must be verified first (Dashboard or `wrangler email routing addresses create`).
|
||||
|
||||
### Reject
|
||||
|
||||
```typescript
|
||||
message.setReject('Your message was blocked');
|
||||
```
|
||||
|
||||
### Reply
|
||||
|
||||
Using `env.EMAIL.send()` (recommended — no extra dependencies):
|
||||
|
||||
```typescript
|
||||
async email(message, env, ctx) {
|
||||
const subject = message.headers.get("subject") || "";
|
||||
await env.EMAIL.send({
|
||||
to: message.from,
|
||||
from: message.to,
|
||||
subject: `Re: ${subject}`,
|
||||
html: "<p>Thanks! We'll respond shortly.</p>",
|
||||
text: "Thanks! We'll respond shortly.",
|
||||
});
|
||||
await message.forward("team@company.com");
|
||||
}
|
||||
```
|
||||
|
||||
Using `message.reply()` with MIME (more control, requires `mimetext` + `nodejs_compat`):
|
||||
|
||||
```typescript
|
||||
import { EmailMessage } from "cloudflare:email";
|
||||
import { createMimeMessage } from "mimetext";
|
||||
|
||||
async email(message, env, ctx) {
|
||||
const msg = createMimeMessage();
|
||||
const messageId = message.headers.get("Message-ID");
|
||||
if (messageId) msg.setHeader("In-Reply-To", messageId);
|
||||
msg.setSender({ name: "Support", addr: "support@yourdomain.com" });
|
||||
msg.setRecipient(message.from);
|
||||
msg.setSubject("Re: " + (message.headers.get("subject") || ""));
|
||||
msg.addMessage({ contentType: "text/plain", data: "Thanks for reaching out!" });
|
||||
|
||||
await message.reply(new EmailMessage("support@yourdomain.com", message.from, msg.asRaw()));
|
||||
}
|
||||
```
|
||||
|
||||
## Parsing Emails
|
||||
|
||||
Use [postal-mime](https://www.npmjs.com/package/postal-mime) to parse raw MIME content:
|
||||
|
||||
```typescript
|
||||
import PostalMime from "postal-mime";
|
||||
|
||||
async email(message, env, ctx) {
|
||||
const rawBuffer = await new Response(message.raw).arrayBuffer();
|
||||
const parsed = await PostalMime.parse(rawBuffer);
|
||||
|
||||
console.log("Subject:", parsed.subject);
|
||||
console.log("Text:", parsed.text);
|
||||
console.log("Attachments:", parsed.attachments.length);
|
||||
}
|
||||
```
|
||||
|
||||
## Store and Reply Later (Human-in-the-Loop)
|
||||
|
||||
A common pattern is to store incoming emails in a Durable Object (SQLite) so a human or AI agent can review and reply later — rather than replying immediately in the `email()` handler. This enables support inboxes, approval workflows, and AI-drafted replies.
|
||||
|
||||
### Architecture
|
||||
|
||||
```
|
||||
Email Routing → email() handler → parse + store in DO → user reviews later → reply via send_email binding
|
||||
```
|
||||
|
||||
The `email()` handler stores the email and returns immediately. Replies happen later via a separate HTTP request or agent action.
|
||||
|
||||
### Receive and Store
|
||||
|
||||
```typescript
|
||||
import PostalMime from 'postal-mime';
|
||||
|
||||
export class MailboxDO extends DurableObject {
|
||||
async storeEmail(
|
||||
from: string,
|
||||
to: string,
|
||||
subject: string,
|
||||
body: string,
|
||||
messageId: string,
|
||||
inReplyTo: string | null
|
||||
) {
|
||||
this.ctx.storage.sql.exec(
|
||||
`INSERT INTO emails (sender, recipient, subject, body, message_id, in_reply_to, date, read)
|
||||
VALUES (?, ?, ?, ?, ?, ?, datetime('now'), 0)`,
|
||||
from,
|
||||
to,
|
||||
subject,
|
||||
body,
|
||||
messageId,
|
||||
inReplyTo
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default {
|
||||
async email(message, env, ctx) {
|
||||
const raw = await new Response(message.raw).arrayBuffer();
|
||||
const parsed = await PostalMime.parse(raw);
|
||||
|
||||
const id = env.MAILBOX.idFromName(message.to);
|
||||
const stub = env.MAILBOX.get(id);
|
||||
|
||||
await stub.storeEmail(
|
||||
message.from,
|
||||
message.to,
|
||||
parsed.subject || '(no subject)',
|
||||
parsed.text || parsed.html || '',
|
||||
message.headers.get('message-id') || '',
|
||||
message.headers.get('in-reply-to') || null
|
||||
);
|
||||
|
||||
// Optionally trigger an AI agent to draft a reply (non-blocking)
|
||||
// ctx.waitUntil(notifyAgent(env, message.to, emailId));
|
||||
}
|
||||
} satisfies ExportedHandler<Env>;
|
||||
```
|
||||
|
||||
### Reply Later
|
||||
|
||||
When a user (or agent) decides to reply, build proper threading headers and send via the `send_email` binding:
|
||||
|
||||
```typescript
|
||||
// In an HTTP handler or agent tool — not in the email() handler
|
||||
async function replyToStoredEmail(env: Env, original: StoredEmail, replyBody: string) {
|
||||
// Build threading headers (In-Reply-To + References per RFC 2822)
|
||||
const headers: Record<string, string> = {};
|
||||
if (original.messageId) {
|
||||
headers['In-Reply-To'] = original.messageId;
|
||||
headers['References'] = original.messageId;
|
||||
}
|
||||
|
||||
await env.EMAIL.send({
|
||||
to: original.sender,
|
||||
from: original.recipient,
|
||||
subject: `Re: ${original.subject}`,
|
||||
text: replyBody,
|
||||
html: `<p>${replyBody}</p>`,
|
||||
headers
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
### Key Design Points
|
||||
|
||||
- **Buffer `message.raw` once**, parse with `postal-mime`, then store structured fields. Don't store the raw stream.
|
||||
- **Extract `Message-ID`, `In-Reply-To`, and `References`** headers during ingest for threading. Fall back to subject-based matching for emails without threading headers.
|
||||
- **Use Durable Object SQLite** for per-mailbox storage — each mailbox gets its own DO instance keyed by email address, providing natural isolation.
|
||||
- **Store attachments separately** in R2 (binary blobs), with metadata in SQLite.
|
||||
- **Defer heavy work** (AI drafting, notifications) via `ctx.waitUntil()` so the `email()` handler returns quickly.
|
||||
- **Never auto-send from the `email()` handler** in a human-in-the-loop flow. Store a draft, let the user review, then send via a separate action.
|
||||
|
||||
## Gotchas
|
||||
|
||||
- **`message.raw` is single-use.** Buffer first: `const raw = await new Response(message.raw).arrayBuffer()`
|
||||
- **Destinations must be verified.** Forwarding to unverified addresses fails silently.
|
||||
- **Handler must act.** If your handler returns without consuming raw, forwarding, or rejecting, the email is dropped.
|
||||
- **DMARC/SPF for replies.** If sending replies, ensure your domain has proper SPF/DKIM records (auto-configured on domain onboarding).
|
||||
@@ -0,0 +1,19 @@
|
||||
# Sending Emails — Workers Binding & Agents SDK
|
||||
|
||||
Prefer the native binding for Workers. For an external application, or when the user explicitly requests HTTP integration, use the [REST API guide](rest-api.md).
|
||||
|
||||
Read the documentation for the selected task before implementing. Inspect the project's installed Wrangler and Agents SDK versions, configuration, and existing conventions first. Run `wrangler types` through the project's package manager after changing bindings; use its generated types instead of handwritten email interfaces. See [Workers TypeScript](https://developers.cloudflare.com/workers/languages/typescript/) for matching types to the project's compatibility date and flags. Do not upgrade dependencies just to match an example.
|
||||
|
||||
| Task | Read |
|
||||
| ------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| Configure the binding, send a message, or maintain existing MIME-based sending | [Workers API](https://developers.cloudflare.com/email-service/api/send-emails/workers-api/) |
|
||||
| Restrict sender or destination addresses | [Configure send bindings](https://developers.cloudflare.com/email-service/configuration/send-bindings/) |
|
||||
| Set multiple recipients, CC/BCC, or named addresses | [Specify recipients](https://developers.cloudflare.com/email-service/examples/email-sending/recipients/) |
|
||||
| Add files, inline images, or uploaded attachments | [Email attachments](https://developers.cloudflare.com/email-service/examples/email-sending/email-attachments/) |
|
||||
| Set custom headers or diagnose header validation | [Email headers](https://developers.cloudflare.com/email-service/reference/headers/) |
|
||||
| Check recipient, message-size, or sending quotas | [Limits](https://developers.cloudflare.com/email-service/platform/limits/) |
|
||||
| Choose local simulation or remote delivery | [Local email sending](https://developers.cloudflare.com/email-service/local-development/sending/) |
|
||||
| Interpret send results and binding errors | [Workers API response and error handling](https://developers.cloudflare.com/email-service/api/send-emails/workers-api/#error-handling) |
|
||||
| Send, receive, route, and securely reply from an Agent | [Email agent walkthrough](https://developers.cloudflare.com/agents/examples/email-agent/) |
|
||||
|
||||
When adapting REST code to a binding, verify address fields, attachment representation, response shape, and errors against the binding docs and generated types. Exercise the relevant success and failure paths using the project's existing checks. Confirm whether the chosen local configuration simulates delivery or sends real mail before testing.
|
||||
@@ -0,0 +1,110 @@
|
||||
---
|
||||
name: cloudflare-one-migrations
|
||||
description: Assess and plan migrations from existing VPN, SWG, or SASE platforms to Cloudflare One, including policy mapping, parity gaps, and rollout.
|
||||
---
|
||||
|
||||
# Cloudflare One Migrations
|
||||
|
||||
Retrieve current Cloudflare docs, Cloudflare API schemas, and source-vendor export docs before generating exact configuration.
|
||||
|
||||
## Workflow
|
||||
|
||||
1. Identify the source stack: Zscaler ZIA, Zscaler ZPA, Palo Alto NGFW/Prisma/GlobalProtect, legacy VPN/SWG/SD-WAN, or other.
|
||||
2. Request exports and logs before mapping. Prefer structured exports over screenshots or prose summaries.
|
||||
3. Build an inventory: identities, groups, apps, destinations, connectors/tunnels, DNS/URL/firewall/DLP/TLS policies, objects/lists, locations/sites, exceptions, hit counts, and compliance logging.
|
||||
4. Produce a mapping plan: source object, Cloudflare One target resource, confidence, prerequisites, unsupported/partial mappings, and manual decisions.
|
||||
5. Create dependencies first: identity/[SCIM](https://developers.cloudflare.com/cloudflare-one/team-and-resources/users/scim/), connectors/on-ramps, routes/DNS, lists/objects, TLS bypasses, Access apps/policies, Gateway policies, DLP/CASB, logging.
|
||||
6. Stage safely: use a migration prefix, create disabled/audit-mode rules by default, pilot with small groups/sites, compare logs, then expand rollout.
|
||||
7. Account for every source rule. Each rule must map to a Cloudflare object or an explicit Not Migrated row with reason and security impact.
|
||||
|
||||
## Exports To Ask For
|
||||
|
||||
- ZIA: URL filtering, firewall filtering, SSL inspection, DLP, custom URL categories, IP groups, network services/service groups, users/groups/departments, locations, GRE tunnels, and static IPs.
|
||||
- ZPA: app segments, segment groups, server groups, app connectors/connector groups, access policies, IdP/group mapping, private DNS domains, ports, and protocols.
|
||||
- Palo Alto/Prisma: security/NAT/decryption rules, address/service objects and groups, URL categories, HIP profiles, GlobalProtect config, Prisma Access remote network/service connection config, zones, tags, logs, and hit counts.
|
||||
|
||||
## Mapping Heuristics
|
||||
|
||||
- ZIA/SWG policies usually map to [Gateway traffic policies](https://developers.cloudflare.com/cloudflare-one/traffic-policies/) and Gateway lists.
|
||||
- ZPA private app access usually maps to [Access application types](https://developers.cloudflare.com/cloudflare-one/access-controls/applications/choose-application-type/), [Cloudflare Tunnel](https://developers.cloudflare.com/cloudflare-one/networks/connectors/cloudflare-tunnel/), private network routing/DNS, and [Access policies](https://developers.cloudflare.com/cloudflare-one/access-controls/policies/).
|
||||
- Palo Alto rules map only after understanding traffic direction, zones, objects, users, apps, decryption, and hit counts. Do not flatten zones blindly into lists.
|
||||
- Legacy VPN replacement is usually Access + Cloudflare One Client / WARP + Tunnel or Mesh for app access. Use [Cloudflare WAN](https://developers.cloudflare.com/cloudflare-wan/) only when site-to-site traffic is required; use the [Network VPN migration design guide](https://developers.cloudflare.com/reference-architecture/design-guides/network-vpn-migration/) and [Replace your VPN](https://developers.cloudflare.com/cloudflare-one/setup/replace-vpn/) docs for current patterns.
|
||||
|
||||
## Migration Assessment Prompts
|
||||
|
||||
- Source coverage: which products are in scope, which exports are available, and whether screenshots/prose summaries are hiding missing object files.
|
||||
- Rule volume and hit data: counts by rule type, disabled/stale rules, no-hit rules, high-hit rules, and business-critical exceptions.
|
||||
- Object dependencies: address objects, service objects, groups, custom categories, network services, app IDs, zones, tags, connectors, and server groups.
|
||||
- Identity readiness: IdP, SCIM/group sync, group-name normalization, individual-user rules, local groups, service accounts, and contractor identities.
|
||||
- TLS/DLP readiness: source decryption rules, certificate-pinned bypasses, [DLP](https://developers.cloudflare.com/cloudflare-one/data-loss-prevention/) engines/profiles, custom regex, exact-match data, and payload logging expectations.
|
||||
- Connectivity readiness: source tunnels/connectors, private DNS, [Split Tunnels](https://developers.cloudflare.com/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/route-traffic/split-tunnels/) or bypass behavior, source IP preservation, [egress IP](https://developers.cloudflare.com/cloudflare-one/traffic-policies/egress-policies/) allowlists, and site-to-site requirements.
|
||||
- Rollout readiness: pilot groups/sites, parallel-run period, rollback owner, source-stack decommission criteria, and monitoring/log comparison plan.
|
||||
|
||||
## Source-Specific Traps
|
||||
|
||||
### Zscaler ZIA / SWG
|
||||
|
||||
- Custom URL categories often split into separate IP, domain, and URL lists. Count the generated lists, not just source categories.
|
||||
- ZIA locations with IPs are useful as source IP lists; they are not automatically [Gateway DNS locations](https://developers.cloudflare.com/cloudflare-one/networks/resolvers-and-proxies/dns/locations/) for DNS policy scoping.
|
||||
- GRE tunnel source IPs can inform policy conditions, but the transport migration is a separate WARP Connector or Cloudflare WAN workstream.
|
||||
- CAUTION/warn behavior has no exact Gateway equivalent. Treat it as an explicit customer decision, not a silent allow/block choice.
|
||||
- DLP engines and custom regex usually require manual Cloudflare DLP profile recreation. Placeholder policies must not be enabled as if DLP is complete.
|
||||
- Network application groups and unsupported protocols are partial mappings. Review them before enablement.
|
||||
- If SCIM is unavailable, identity-scoped source rules become overly broad unless you add an enforceable alternative such as user/email lists. Check [Gateway identity selectors](https://developers.cloudflare.com/cloudflare-one/traffic-policies/identity-selectors/) before creating those rules.
|
||||
|
||||
### Zscaler ZPA / Private Access
|
||||
|
||||
- ZPA app segments, server groups, and connector groups do not map 1:1. Cloudflare separates Access apps, tunnel routes, DNS, and policies.
|
||||
- Creating tunnels through the API does not complete connector deployment. Plan cloudflared installation, authentication, and origin reachability separately.
|
||||
- Create one Cloudflare Tunnel per ZPA connector group regardless of connector runtime status (AUTHENTICATED, DISCONNECTED, or disabled). Status is operational, not architectural. Tag disconnected or legacy groups in the tunnel description and let the customer decide what to decommission after validation.
|
||||
- Each ZPA connector instance within a group maps to one cloudflared replica running against that tunnel's token. Match replica count to connector instance count per group to preserve the same topology. A single tunnel token supports multiple simultaneous cloudflared processes. Recommend installing replicas within the same data center but on different hosts or subnets.
|
||||
- For each connector group, identify all server groups linked to it and all app segments assigned to those server groups. IP addresses and CIDRs in those app segments become CIDR routes on the corresponding tunnel; domain names become hostname routes on the same tunnel. Prefer one CIDR route per subnet over per-host /32 routes where a broad subnet covers all app segment IPs.
|
||||
- ZPA bypass means split-tunnel bypass in Cloudflare, not an Access `bypass` decision. Bypass rules map to WARP [Split Tunnel](https://developers.cloudflare.com/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/route-traffic/split-tunnels/) exclude entries. This is a manual configuration step with no API automation - the customer must add bypassed domains and IPs to the device profile split tunnel exclude list through the dashboard.
|
||||
- Agentless/browser apps may become separate public-hostname Access apps per domain. WARP private apps remain private-destination apps.
|
||||
- The default Cloudflare Access application destination limit is 5 hostnames per app. For ZPA migrations with large app segments, contact the Cloudflare account team to request an increase (up to 50) before implementation. Confirm the limit is active on the account before creating apps - without it, large segments must be split into multiple apps with identical policies, significantly increasing object count.
|
||||
- IP-anchored apps require an explicit egress decision before migration: preserve source IP through customer egress, use Cloudflare [dedicated egress](https://developers.cloudflare.com/cloudflare-one/traffic-policies/egress-policies/) where available, or accept that the target service must be updated to allow new source IPs. This is a customer decision that blocks implementation if unresolved.
|
||||
- Resolver policies can be account-wide. Be careful with overlapping private DNS namespaces across sites or virtual networks; retrieve [resolver policy](https://developers.cloudflare.com/cloudflare-one/traffic-policies/resolver-policies/) docs before making DNS changes.
|
||||
- Each ZPA access policy rule maps to a Cloudflare reusable Access policy. Create all reusable policies before attaching them to Access apps. In default-deny Gateway Network environments, additionally create a Network allow rule with selector "Self-hosted Access App with Private Address is Present" (wirefilter: `any(access.private_app[*] in {"*"})`) at higher precedence than any broad L4 block rules - without it, Gateway blocks private app traffic before Access policy evaluation occurs.
|
||||
- In combined ZIA and ZPA migrations, Gateway Network rules can accidentally block Access private-app traffic. The Gateway Network allow rule above is the fix - place it at higher precedence (lower number) than ZIA-migrated block rules. Add and validate this rule before enabling broad L4 blocks.
|
||||
|
||||
### Palo Alto / Prisma / NGFW
|
||||
|
||||
- One Palo Alto rule can produce multiple Cloudflare resources. Preserve rule intent, not rule count.
|
||||
- App-ID, URL category, zone, HIP, schedule, and decryption behavior rarely translate exactly. Mark partial mappings rather than forcing false equivalence.
|
||||
- Export address/service objects and groups with rules. Missing object exports cause silent-looking drops unless explicitly detected.
|
||||
- Broad `any` destination/service rules and very broad CIDRs require manual review. Do not auto-create broad catchalls.
|
||||
- HIP/device checks require Cloudflare [device posture](https://developers.cloudflare.com/cloudflare-one/reusable-components/posture-checks/) integrations before enforcement.
|
||||
|
||||
## Gotchas
|
||||
|
||||
- Source exports often split references across files. Resolve IDs against object, service, and group files before declaring a rule unmappable.
|
||||
- Individual users, local groups, departments, and dynamic application IDs often need identity normalization. SCIM/group sync is the gating prerequisite for group selectors.
|
||||
- Zscaler caution/warn behavior, Palo Alto App-ID behavior, and TLS/decryption exceptions may not have exact equivalents. Flag them as decision points instead of forcing a 1:1 mapping.
|
||||
- Preserve source rule order and hit counts where available. Disable or delete stale/no-hit rules only with user approval.
|
||||
- Never create broad allow-all catchalls to preserve connectivity unless explicitly requested and time-limited.
|
||||
|
||||
## Validation Gates
|
||||
|
||||
- After each migration stage, compare Cloudflare object counts against parsed source counts. Stop on mismatches.
|
||||
- Review every `unsupported`, `partial`, `unmapped`, `needs_identity`, `needs_posture`, and `manual_review` item before enabling policies.
|
||||
- Validate group matching with real pilot users after SCIM sync and re-authentication.
|
||||
- Test TLS inspection and Do Not Inspect behavior before enabling HTTP/DLP blocks broadly.
|
||||
- Keep rollback paths explicit: disable migrated rules by prefix, restore source routing, or revert the pilot group/site.
|
||||
- Before declaring done, produce a source-rule accounting table: migrated object, partial mapping, not migrated reason, security impact, and owner for each manual action.
|
||||
|
||||
## Assessment Template
|
||||
|
||||
```markdown
|
||||
## Migration Assessment
|
||||
|
||||
Source stack:
|
||||
Artifacts reviewed:
|
||||
Assumptions / missing exports:
|
||||
Recommended Cloudflare One target:
|
||||
Mapping summary:
|
||||
Risks / partial mappings:
|
||||
Not migrated:
|
||||
Pilot plan:
|
||||
Validation:
|
||||
Rollback:
|
||||
```
|
||||
@@ -0,0 +1,178 @@
|
||||
---
|
||||
name: cloudflare-one
|
||||
description: Design, configure, troubleshoot, or review Cloudflare One Zero Trust and SASE deployments. Use cloudflare-one-migrations for migration planning from other vendors.
|
||||
---
|
||||
|
||||
# Cloudflare One
|
||||
|
||||
Before citing limits, settings, API fields, category IDs, or exact UI paths, retrieve current information from the [Cloudflare One docs](https://developers.cloudflare.com/cloudflare-one/), the Cloudflare docs MCP server, or the Cloudflare API schema.
|
||||
|
||||
## Workflow
|
||||
|
||||
1. Classify the ask: architecture, configuration, troubleshooting, migration, or review.
|
||||
2. Gather context: account ID, users/sites/apps, identity provider, SCIM/group sync, device management, traffic path, compliance constraints, and rollout blast radius.
|
||||
3. Retrieve only the current docs needed for the products involved: Access, Gateway, WARP/device client, Tunnel/Mesh, Cloudflare WAN, DLP, CASB, device posture, or identity.
|
||||
4. If account access is available, inspect existing resources before proposing or making changes: Access apps/policies/groups/IdPs, Gateway rules/lists/categories, device profiles/posture checks, tunnels/routes, DNS/resolver settings, and locations/sites.
|
||||
5. Propose the change set with prerequisites, validation, and rollback. For risky changes, stage disabled or scoped to a pilot group/site unless the user explicitly asks otherwise.
|
||||
|
||||
## Assessment Prompts
|
||||
|
||||
Use these to avoid jumping straight to configuration. Ask only the prompts relevant to the user's task.
|
||||
|
||||
### Architecture and Current State
|
||||
|
||||
- Sites and users: offices, branches, data centers, VPCs, remote users, contractors, user counts, and current connectivity model.
|
||||
- Applications and destinations: SaaS, public apps, private apps, APIs, infrastructure targets, protocols, ports, hostnames, and IP ranges.
|
||||
- Connectivity: VPN, MPLS, SD-WAN, direct Internet breakout, centralized backhaul, site-to-site needs, and private DNS architecture.
|
||||
- Security stack: current SWG, NGFW, VPN/ZTNA, DLP, CASB, email security, logging, and compliance requirements.
|
||||
- Identity: IdP, SCIM/group sync, group naming, multi-IdP needs, service accounts, and contractor/partner access.
|
||||
- Rollout: pilot users/sites, blast radius, rollback path, support owners, and success criteria.
|
||||
|
||||
### Access and SaaS Federation
|
||||
|
||||
- App shape: web app, API, SSH/RDP/VNC, database, SaaS app, public hostname, private IP, or private hostname. Retrieve [Access application type](https://developers.cloudflare.com/cloudflare-one/access-controls/applications/choose-application-type/) docs before choosing.
|
||||
- Access model: clientless browser access, private networking with device client, peer to peer connectivity, service connections with service tokens or mutual TLS, or SaaS SSO federation.
|
||||
- Policy needs: user groups, device posture, session duration, mTLS, service tokens, and app launcher visibility. Retrieve [Access policy](https://developers.cloudflare.com/cloudflare-one/access-controls/policies/) docs before configuring selectors or evaluation order.
|
||||
- SaaS details: SAML vs OIDC support, ACS/redirect URLs, Entity IDs/client IDs, required attributes, and tenant-control requirements.
|
||||
|
||||
### Tunnel and Private Networking
|
||||
|
||||
- Sites and segments: which data centers, VPCs, offices, or network segments need connectivity.
|
||||
- HA: dev/test single connector, production multiple connectors, or advanced multi-tunnel/site redundancy.
|
||||
- Runtime: where cloudflared or WARP Connector/Mesh will run: VM, container, Kubernetes, bare metal, or other target.
|
||||
- Egress: whether connectors can reach Cloudflare over the required outbound ports/protocols. Retrieve [Tunnel connectivity prechecks](https://developers.cloudflare.com/cloudflare-one/networks/connectors/cloudflare-tunnel/troubleshoot-tunnels/connectivity-prechecks/) before naming exact endpoints.
|
||||
- Origin reachability: whether the connector can resolve and reach every private origin.
|
||||
- Routing: required CIDRs/hostnames, overlapping IP spaces, [virtual networks](https://developers.cloudflare.com/cloudflare-one/networks/connectors/cloudflare-tunnel/private-net/cloudflared/tunnel-virtual-networks/), [Split Tunnels](https://developers.cloudflare.com/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/route-traffic/split-tunnels/), and private DNS/[resolver policy](https://developers.cloudflare.com/cloudflare-one/traffic-policies/resolver-policies/) needs.
|
||||
- Management model: prefer remotely managed/token-based tunnels for new deployments unless there is a clear reason for local config.
|
||||
|
||||
### Gateway, TLS, and DLP
|
||||
|
||||
- Traffic controls: DNS categories, HTTP URL/path inspection, L4 ports/protocols, egress IP requirements, custom lists, and allow/block exceptions. Retrieve [Gateway traffic policy](https://developers.cloudflare.com/cloudflare-one/traffic-policies/) docs for current selectors and order of enforcement.
|
||||
- Identity: whether Gateway policies need user or group selectors, and whether users will be authenticated through WARP/IdP context. Check [Gateway identity selectors](https://developers.cloudflare.com/cloudflare-one/traffic-policies/identity-selectors/) and [SCIM provisioning](https://developers.cloudflare.com/cloudflare-one/team-and-resources/users/scim/) when groups are involved.
|
||||
- TLS inspection: root CA deployment path, certificate-pinned applications, compliance exceptions, and FIPS requirements. Retrieve [TLS decryption](https://developers.cloudflare.com/cloudflare-one/traffic-policies/http-policies/tls-decryption/) docs before enabling.
|
||||
- DLP: sensitive data types, channels to inspect, TLS inspection readiness, DLP profiles, payload logging requirements, and false-positive tolerance. Retrieve [DLP](https://developers.cloudflare.com/cloudflare-one/data-loss-prevention/) docs before creating enforcement.
|
||||
|
||||
### CASB, Device Posture, and Risk
|
||||
|
||||
- CASB: SaaS vendors, admin access level, scan policy, org size, remediation owner, and whether inline protection is also required. Retrieve [CASB findings](https://developers.cloudflare.com/cloudflare-one/cloud-and-saas-findings/manage-findings/) docs before recommending remediation.
|
||||
- Device posture: required checks, third-party EDR/MDM integrations, enrollment rules, device profiles, and split tunnel alignment.
|
||||
- Risk scoring: relevant behavior signals, false-positive sources such as VPNs or service accounts, and whether risk is for investigation or enforcement. Retrieve [user risk score](https://developers.cloudflare.com/cloudflare-one/team-and-resources/users/risk-score/) docs before using risk in policies.
|
||||
|
||||
### Cloudflare WAN / Site Connectivity
|
||||
|
||||
- Site topology, on-ramp type, route ownership, tunnel redundancy, static vs BGP-managed routes, network firewall needs, and appliance/profile ownership. Retrieve [Cloudflare WAN](https://developers.cloudflare.com/cloudflare-wan/) and [Cloudflare Network Firewall](https://developers.cloudflare.com/cloudflare-network-firewall/) docs before proposing site connectivity changes.
|
||||
|
||||
## Guardrails
|
||||
|
||||
- Access controls application authorization; Gateway controls traffic inspection/filtering. Use both when the requirement spans identity-aware app access and network/web security.
|
||||
- For new Access deployments, create policies through the reusable policy API (`/access/policies`) and attach them to applications. Do not send inline `policies` in an application create/update request unless the current API documentation explicitly requires an app-scoped policy.
|
||||
- Treat an app-scoped policy reported as `reusable: false` as legacy. Migrate existing policies with the documented `make_reusable` endpoint or replace them with reusable policies; do not create new legacy policies. Distinguish legacy policies from the deprecated legacy private-network application type.
|
||||
- Public hostname Access apps can be clientless. Private destination apps require WARP/Device client or another network on-ramp plus routes and DNS resolution. Retrieve [self-hosted private app](https://developers.cloudflare.com/cloudflare-one/access-controls/applications/non-http/self-hosted-private-app/) docs before configuring private destinations.
|
||||
- Cloudflare Tunnel is an off-ramp from a private network to Cloudflare. Cloudflare WAN and Mesh are other off-ramps which can also be on-ramps.
|
||||
- Group-based policies depend on IdP group claims or SCIM. If group sync is missing, do not invent group selectors.
|
||||
- Private hostnames need explicit DNS routing/resolution; creating an Access app alone is not enough. Use [resolver policies](https://developers.cloudflare.com/cloudflare-one/traffic-policies/resolver-policies/) and review [Connect a private hostname](https://developers.cloudflare.com/cloudflare-one/networks/connectors/cloudflare-tunnel/private-net/cloudflared/connect-private-hostname/)
|
||||
- HTTP inspection and DLP for encrypted web traffic require TLS inspection and planned Do Not Inspect exceptions.
|
||||
- Gateway DNS, Network, HTTP, and Egress policies have different evaluation semantics. Retrieve [order of enforcement](https://developers.cloudflare.com/cloudflare-one/traffic-policies/order-of-enforcement/) docs before explaining precedence.
|
||||
- Start broad block/allow/DLP/TLS policies disabled limited to a pilot with specific target users or groups unless the user approves a wider rollout.
|
||||
|
||||
### Identity and Access
|
||||
|
||||
- Access Groups are Cloudflare objects; IdP/SCIM groups are identity claims. Gateway group selectors use synced IdP groups, not Access Groups.
|
||||
- Group names and SAML/OIDC attributes are case-sensitive. Verify exact claim names and values before creating group-based rules.
|
||||
- SCIM changes and group membership can be stale until sync and re-authentication complete. Troubleshoot with the user's last authenticated identity, not just the IdP state.
|
||||
- Access policies are default-deny. A private app with routes but no Allow policy still blocks access.
|
||||
- Access policy selectors can use IP lists, not Gateway domain or URL lists.
|
||||
- SaaS federation handles authentication into the SaaS app. SaaS authorization and tenant restrictions usually require SaaS-side roles and/or Gateway tenant controls.
|
||||
- Browser Rendering for SSH/VNC/RDP is an Access capability. Browser Isolation renders general web content remotely. Do not conflate them.
|
||||
|
||||
### Device Client Deployment
|
||||
|
||||
- The [Cloudflare One device client](https://developers.cloudflare.com/cloudflare-one/team-and-resources/devices/cloudflare-one-client/) is the on-ramp for user devices. Two components control it: **enrollment rules** (who can connect) and **device profiles** (how the client behaves after enrollment).
|
||||
- The enrollment rule is an Access application of type `warp`, not a device setting. It accepts reusable Access policies. Look in Access for enrollment debugging, not Devices.
|
||||
- For headless or autonomous devices (services, kiosks, Linux hosts), use service token enrollment. Non-human devices authenticate as `non_identity@[team-domain].cloudflareaccess.com` and have no group membership - device profiles targeting IdP groups will not match them. Target headless devices explicitly with the non-identity email, specific conventions about the devices (OS information, etc.),or let them fall to the default profile.
|
||||
- Device profiles control connection mode, [split tunnel](https://developers.cloudflare.com/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/route-traffic/split-tunnels/) configuration, user permissions (disable, switch lock), auto-reconnect, and captive portal behavior. Profiles are matched by user group or device attributes in precedence order - first match wins, default profile catches the rest.
|
||||
- Split tunnel mode is the single most impactful client setting. Choose the mode based on the deployment goal:
|
||||
|
||||
| Goal | Mode | Rationale |
|
||||
| ----------------------------------- | ------------- | ----------------------------------------------------------------------------------------------------------------------- |
|
||||
| VPN replacement only (private apps) | **Include** | Route only specified private CIDRs and hostnames through the client. Everything else goes direct. Minimal blast radius. |
|
||||
| SWG only (internet security) | **Exclude** | All traffic through the client. Exclude only what breaks (local printers, certificate-pinned apps). |
|
||||
| VPN replacement + SWG | **Exclude** | All traffic through the client. Most common enterprise configuration. |
|
||||
| Coexistence with another VPN | **Include** | Avoids conflict with the other VPN's tunnel interface and DNS control. |
|
||||
| DNS filtering only | DNS-only mode | Only DNS queries go to Gateway. No traffic proxying. |
|
||||
|
||||
- Include vs exclude is per-profile, not per-entry. You cannot mix modes in the same profile. Switching modes mid-deployment requires re-evaluating every entry.
|
||||
- Split tunnel entries must align with tunnel routes bidirectionally. A CIDR in the include list without a matching tunnel route causes a black hole. A tunnel route without a matching device profile entry means traffic never enters the tunnel.
|
||||
- MDM parameters (`mdm.xml` / managed preferences) override dashboard-configured profile settings for any setting specified in the file. If dashboard changes appear to have no effect on managed devices, check MDM config. Retrieve [MDM deployment](https://developers.cloudflare.com/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/mdm-deployment/) docs for platform-specific file locations and parameters.
|
||||
- If another VPN client or agent controls DNS on the device, the device client's DNS interception will conflict. In coexistence scenarios, use "traffic only" mode to avoid routing table and DNS conflicts.
|
||||
- Captive portal detection temporarily disconnects the client when it detects a portal (hotel WiFi, airport). This is a common source of end-user friction and should be managed carefully.
|
||||
|
||||
### Private Networking
|
||||
|
||||
- Split tunnel mode changes the meaning of every route decision: Exclude mode sends traffic to Cloudflare when removed from excludes; Include mode sends traffic only when added to includes.
|
||||
- Virtual networks should be used primarily when IP subnets overlap and hostname-based routing is not used. It can be used to control other user connectivity behavior, but it is recommended to manage through security policies.
|
||||
- A healthy tunnel only proves cloudflared can reach Cloudflare. The tunnel must have appropriate published application routes, network routes, or hostname routes for connectivity to function.
|
||||
- Cloudflare Tunnel and Cloudflare Mesh can both be used to facilitate connectivity to internal networks. Cloudflare WAN can as well, but it is gated behind Enterprise subscriptions. Retrieve [choose an on-ramp](https://developers.cloudflare.com/learning-paths/secure-internet-traffic/connect-devices-networks/choose-on-ramp/) when deliberating between Tunnel types.
|
||||
- Run multiple cloudflared connectors for production HA, preferably on separate hosts. Token-based, remotely managed tunnels are the default for new deployments.
|
||||
|
||||
### Gateway, TLS, and DLP
|
||||
|
||||
- `dns.domains` matches a domain and subdomains; `dns.fqdn` is exact-match only.
|
||||
- DNS pre-resolution selectors and post-resolution selectors do not behave like a single strict precedence list. Retrieve current evaluation docs before changing rule order.
|
||||
- HTTP Do Not Inspect rules run before HTTP Allow/Block/Isolate behavior. A later block rule will not override an earlier inspection bypass.
|
||||
- Certificate-pinned apps need Do Not Inspect exceptions before broad TLS inspection. Deploy the Cloudflare root CA to managed devices before enabling inspection.
|
||||
- DLP profiles are detection definitions only. They do nothing until referenced by Gateway HTTP policies or CASB scan settings. Rules with body inspection may be evaluated multiple times in a single pass.
|
||||
- Start DLP with payload logging where appropriate, tune false positives, then block.
|
||||
- Gateway Network policies are strict L4 controls. Identity-aware L4 matching requires authenticated device context.
|
||||
|
||||
### CASB, Risk, and Operations
|
||||
|
||||
- API CASB is out-of-band and periodic. It does not provide real-time inline enforcement although some integrations support "remediation"; use Gateway granular application controls for inline CASB capability for supported applications. Retrieve [Granular application controls](https://developers.cloudflare.com/cloudflare-one/traffic-policies/http-policies/granular-controls/) when creating security policies for specific actions in specific SaaS applications.
|
||||
- CASB findings are tied to specific assets and instances. Drill into affected assets before recommending remediation.
|
||||
- Use current Dashboard remediation guidance for CASB fixes. Most remediations happen in the SaaS admin console, not Cloudflare.
|
||||
- Large SaaS integrations can take 24-48 hours for initial scans. Reauthorizing can restart scan state; check credential health before reconnecting.
|
||||
- User risk scores are behavior-based and asynchronous. CASB findings do not automatically imply high user risk.
|
||||
|
||||
### Infrastructure Access
|
||||
|
||||
- [Zero Trust Infrastructure Access](https://developers.cloudflare.com/cloudflare-one/access-controls/applications/non-http/infrastructure-apps/) (ZTIA) is the purpose-built offering for SSH access through the device client. It provides capabilities not available through self-hosted apps: keystroke logging, control over how users authenticate to the target machine, [short-lived certificates](https://developers.cloudflare.com/cloudflare-one/networks/connectors/cloudflare-tunnel/use-cases/ssh/ssh-infrastructure-access/#generate-a-cloudflare-ssh-ca) that replace static SSH keys with ephemeral certs tied to Access identity, and lightweight privileged access management. Use Infrastructure Access apps for SSH when the device client is deployed.
|
||||
- [Browser Rendering](https://developers.cloudflare.com/cloudflare-one/access-controls/applications/non-http/browser-rendering/) provides clientless SSH, RDP, and VNC through the browser without requiring the device client. Clientless RDP includes session recording and file transfer controls. Use clientless access when a device client cannot be installed (contractors, partner access, unmanaged devices) - typically not as the default for managed users with the client installed.
|
||||
- [Audit SSH](https://developers.cloudflare.com/cloudflare-one/networks/connectors/cloudflare-tunnel/use-cases/ssh/ssh-infrastructure-access/#enable-ssh-command-logging) is a Gateway Network policy action that logs SSH commands without blocking. It requires the session to be proxied through Cloudflare.
|
||||
- Short-lived certificates require CA configuration on the target host and `sshd` configured to trust the Cloudflare CA public key. Retrieve [short-lived certificate setup](https://developers.cloudflare.com/cloudflare-one/identity/users/short-lived-certificates/) docs before configuring.
|
||||
- For kubectl and database access behind private networks, use the device client with private destination routing. There is no Infrastructure Access or browser-rendered equivalent for arbitrary TCP protocols today.
|
||||
|
||||
### Logs, Analytics, and DEX
|
||||
|
||||
- [Gateway activity logs](https://developers.cloudflare.com/cloudflare-one/analytics/logs/gateway-logs/) record DNS, HTTP, and Network policy decisions. Filter by rule name, user identity, destination, action, and time range. These are the primary troubleshooting tool for "why was this blocked/allowed."
|
||||
- [Access audit logs](https://developers.cloudflare.com/cloudflare-one/insights/logs/dashboard-logs/access-authentication-logs/) record authentication decisions per app - who authenticated, which policy matched, and session details. Use for verifying policy behavior and investigating access failures.
|
||||
- [Shadow IT discovery](https://developers.cloudflare.com/cloudflare-one/insights/analytics/shadow-it-discovery/) uses Gateway HTTP logs to surface unmanaged SaaS applications. Requires TLS inspection for HTTPS visibility.
|
||||
- [DEX (Digital Experience Monitoring)](https://developers.cloudflare.com/cloudflare-one/insights/dex/) provides fleet-level and per-device connectivity diagnostics. Use [DEX tests](https://developers.cloudflare.com/cloudflare-one/insights/dex/tests/) (HTTP, traceroute) to proactively monitor reachability to critical origins and internal apps. Fleet status shows device client health, connection mode, and connectivity state across the enrolled population.
|
||||
- [Logpush](https://developers.cloudflare.com/cloudflare-one/analytics/logs/logpush/) exports Gateway, Access, Network, and DEX logs to external SIEM or storage. Configure before go-live if the customer requires centralized log retention or compliance reporting.
|
||||
- When troubleshooting, work from logs toward config: identify the log entry showing the failure (Gateway block, Access deny, tunnel error, DNS resolution miss), then trace back to the responsible rule, route, or policy.
|
||||
|
||||
### Cloudflare WAN / Site Connectivity
|
||||
|
||||
- Cloudflare WAN is connectivity, not a security service. Apply inspection and policy with Gateway and Network Firewall where required.
|
||||
- WAN firewall expressions are not the same language as Gateway wirefilter expressions. Retrieve the current syntax before editing.
|
||||
- Generated IPsec PSKs and some OAuth/client secrets are returned once. Store them immediately.
|
||||
|
||||
## Output Defaults
|
||||
|
||||
- Designs: current assumptions, target architecture, product responsibilities, rollout phases, validation, and open decisions.
|
||||
- Configuration work: prerequisites, exact resources to inspect/create/change, test cases, and rollback.
|
||||
- Troubleshooting: traffic path, likely failure point, evidence to collect, and next test.
|
||||
|
||||
## Validation Prompts
|
||||
|
||||
- Access: test authorized, unauthorized, posture-failing, service-token, and multi-IdP flows when applicable; inspect logs and policy precedence. For new policies, verify they are managed through the reusable policy collection and not returned as `reusable: false` app-scoped policies.
|
||||
- Private network access: verify route lookup, tunnel health, origin reachability, split tunnel behavior, DNS resolution, and end-to-end access from a device client test device.
|
||||
- Gateway: verify rule type, action, traffic expression, precedence/evaluation phase, referenced lists, and Gateway settings before enabling broadly.
|
||||
- TLS/DLP: test Do Not Inspect exceptions and root CA trust before enabling inspection; test DLP with known samples and monitor false positives before blocking.
|
||||
- CASB/risk: confirm integration health, credential expiry, asset discovery, scan timing, finding instances, and risk-score signal latency before declaring remediation complete.
|
||||
- Cloudflare WAN: verify tunnel health, route priority/ownership, traffic flow, firewall expression syntax, and connector/appliance telemetry where applicable.
|
||||
|
||||
## API Safety
|
||||
|
||||
- Use fully qualified MCP tool names when MCP tools are available.
|
||||
- Never guess category IDs, application IDs, wirefilter fields, or API request bodies. Retrieve the current schema/docs and existing account objects.
|
||||
- Do not enable broad production policies without explicit approval.
|
||||
@@ -0,0 +1,139 @@
|
||||
---
|
||||
name: cloudflare
|
||||
description: Discover and choose Cloudflare products for apps, APIs, AI agents, storage, networking, and security. Use for architecture and product selection, including when the user describes a need without naming a Cloudflare product; then find the relevant skill or documentation.
|
||||
---
|
||||
|
||||
# Discover and build with Cloudflare
|
||||
|
||||
Help agents discover what they can build with Cloudflare and choose the products that fit. Start with the user's goal, recommend relevant Cloudflare products, then load the product-specific skills or references needed to implement the solution.
|
||||
|
||||
## Help the user find the right product
|
||||
|
||||
- Actively surface Cloudflare products that solve the stated problem, even when the user has not named them. Explain the role each recommended product plays and why it fits.
|
||||
- Use the need-to-product map below to choose products, then load the relevant skills or documentation for implementation. A user asking for uploads, background jobs, or document search may not know to ask for R2, Queues, Workflows, or AI Search.
|
||||
- Recommend a small, coherent combination when the task spans products. Add a product when it addresses a concrete requirement; respect the user's existing stack and explicit choices.
|
||||
- When similar products could fit, explain the deciding requirement: data shape, consistency, coordination, execution lifecycle, or how much infrastructure the user wants to manage. Check current availability, limits, and pricing before promising a fit.
|
||||
|
||||
## What are you trying to build?
|
||||
|
||||
**Recommend Workers and [Workers Static Assets](https://developers.cloudflare.com/workers/static-assets/) for new websites and applications, including static sites, SPAs, and full-stack apps.** Workers can do everything Pages can do, and is recommended for all new projects. Preserve existing Pages deployments during unrelated maintenance.
|
||||
|
||||
Find the row closest to the user's task. Products can appear in multiple rows, and a solution can combine products. Read the linked reference or docs before implementing; load named skills when installed. Local links open bundled references: start with the README, then follow configuration, API, pattern, or gotcha links as needed. If a named skill is unavailable, use the relevant product docs through the [Cloudflare directory](https://developers.cloudflare.com/directory/); sibling skills are optional.
|
||||
|
||||
| What you need to do | Product or tool to consider | When to choose it | Skill or reference |
|
||||
| ---------------------------------------------------------------------------------------- | -------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| Choose the building blocks for an AI application | AI overview | Compare Cloudflare's AI services before choosing inference, retrieval, or agent tooling | [AI docs](https://developers.cloudflare.com/ai/) |
|
||||
| Choose infrastructure for a customer-facing platform | Cloudflare for Platforms | Compare running customer code with serving an app on customer domains | [Platform overview](https://developers.cloudflare.com/cloudflare-for-platforms/) |
|
||||
| Choose an approach to live audio and video | Realtime | Compare application SDKs, media infrastructure, and connectivity relays | [Realtime overview](https://developers.cloudflare.com/realtime/) |
|
||||
| Start a Worker or framework project | C3 | Scaffold a project using the appropriate framework template | [C3](references/c3/README.md); `wrangler` skill |
|
||||
| Build or deploy a Next.js app on Cloudflare | vinext + Workers | Use vinext rather than OpenNext for new projects | [nextjs-on-cloudflare skill](../nextjs-on-cloudflare/SKILL.md); [Next.js docs](https://developers.cloudflare.com/workers/framework-guides/web-apps/nextjs/) |
|
||||
| Host a new static site, SPA, or full-stack app | Workers + Workers Static Assets | Serve site files and add server-side logic where needed | [Static Assets](references/static-assets/README.md); `workers-best-practices` skill |
|
||||
| Build an API or handle webhooks | Workers | Run request handlers with access to Cloudflare services | `workers-best-practices` skill; [Workers docs](https://developers.cloudflare.com/workers/) |
|
||||
| Control team, CI, or service-account access to Developer Platform resources | Roles, scopes, and permission policies | Choose the least-privilege role and a scope supported for the member, User Group, or API token | [Roles and permissions](https://developers.cloudflare.com/workers/authorization/); `wrangler` skill for CLI access |
|
||||
| Maintain an existing Pages deployment | Pages + Pages Functions | Update an existing site or its server endpoints; use Workers for new projects | [Pages](references/pages/README.md); [Pages Functions](references/pages-functions/README.md) |
|
||||
| Move a Pages project to Workers | Workers + Workers Static Assets | The task calls for migrating the hosting platform | [Pages migration guide](https://developers.cloudflare.com/workers/static-assets/migration-guides/migrate-from-pages/) |
|
||||
| Let customers deploy code on your platform | Workers for Platforms | Run and manage customer Workers with per-customer controls | [Workers for Platforms](references/workers-for-platforms/README.md) |
|
||||
| Let customers use their own domains with your app | Cloudflare for SaaS | Manage custom hostnames, TLS certificates, and origin routing; check hostname validation and apex-domain plan requirements. Combine with Workers for Platforms when customers also deploy code | [SaaS docs](https://developers.cloudflare.com/cloudflare-for-platforms/cloudflare-for-saas/) |
|
||||
| Connect a Worker to storage or another service | Bindings | Give the Worker access to configured resources through its environment | [Bindings](references/bindings/README.md) |
|
||||
| Run containerized services or Linux software | Containers | The workload needs a container image or software outside the Workers runtime | [Containers](references/containers/README.md) |
|
||||
| Execute generated or untrusted code, build Code Mode tools, or create on-demand previews | Dynamic Workers | Load code at runtime in isolated Workers; check bindings, egress controls, and resource limits. Choose Sandbox when execution needs Linux or shell tools | [Dynamic Workers docs](https://developers.cloudflare.com/dynamic-workers/) |
|
||||
| Give an agent a shell, filesystem, or interactive development environment | Sandbox SDK | Code execution needs a Linux environment or container tools; inspect the package line first | `sandbox-next` for new or preview projects; `sandbox-stable` for existing stable apps; [Sandbox docs](https://developers.cloudflare.com/sandbox/) |
|
||||
| Upgrade a stable Sandbox app to the preview API | Sandbox SDK | The user wants the stable-to-next migration | `sandbox-migrate-to-next` skill; [migration guide](https://developers.cloudflare.com/sandbox/1-0-preview/migrate/) |
|
||||
| Coordinate chat rooms, games, collaborative documents, or bookings | Durable Objects | Operations need shared state and coordination per room, document, or entity | `durable-objects` skill; [Durable Objects docs](https://developers.cloudflare.com/durable-objects/) |
|
||||
| Store and recover state inside a Durable Object | Durable Object storage | Choose storage APIs, transactions, and recovery for coordinated per-entity data | [DO storage](references/do-storage/README.md) |
|
||||
| Store application records and query them with SQL | D1 | Use a managed relational database; use Durable Objects when per-entity coordination is central | [D1](references/d1/README.md) |
|
||||
| Connect to an existing PostgreSQL or MySQL database | Hyperdrive | Keep the existing database and optimize connections from Workers | [Hyperdrive](references/hyperdrive/README.md) |
|
||||
| Distribute configuration or other key-value data | KV | Read-heavy key-value access fits the workload's consistency requirements | [KV](references/kv/README.md) |
|
||||
| Store uploads, downloads, or large objects | R2 | Store files by object key; pair with D1 when searchable metadata needs SQL | [R2](references/r2/README.md) |
|
||||
| Store versioned file trees, agent checkpoints, or repositories | Artifacts | Files need versioning and Git-compatible access; currently closed beta, so confirm access before implementation | [Artifacts](references/artifacts/README.md) |
|
||||
| Ingest event streams into a data lake | Pipelines | Transform and deliver streaming records into R2 | [Pipelines](references/pipelines/README.md) |
|
||||
| Manage Iceberg tables in R2 | R2 Data Catalog | Organize tables for a data lake and compatible query engines | [R2 Data Catalog](references/r2-data-catalog/README.md) |
|
||||
| Query a data lake with SQL | R2 SQL | Analyze data in R2 Data Catalog rather than transactional application records | [R2 SQL](references/r2-sql/README.md) |
|
||||
| Cache application responses | Workers Cache | Default for application caching; check the patterns and limitations before choosing alternatives | [Workers Cache](https://developers.cloudflare.com/workers/cache/); see caching guidance below |
|
||||
| Accelerate an existing website and control cached content | Cache/CDN | Configure caching for a proxied origin using Cache Rules, expiration settings, and purging | [Cache/CDN docs](https://developers.cloudflare.com/cache/) |
|
||||
| Keep origin content in a persistent cache | Cache Reserve | Reduce origin fetches with persistent CDN cache storage | [Cache Reserve](references/cache-reserve/README.md) |
|
||||
| Process jobs asynchronously or buffer bursts of work | Queues | Decouple producers and consumers; use Workflows for durable multi-step orchestration | [Queues](references/queues/README.md) |
|
||||
| Run a job that retries, waits, and resumes across steps | Workflows | Coordinate durable multi-step business processes | [Workflows](references/workflows/README.md) |
|
||||
| Start a Worker on a recurring schedule | Cron Triggers | Trigger scheduled work; combine with Queues or Workflows for the work itself | [Cron Triggers](references/cron-triggers/README.md) |
|
||||
| Run language, embedding, image, or speech models | Workers AI | Use managed inference; verify model capabilities, schemas, and pricing | [Workers AI](references/workers-ai/README.md) |
|
||||
| Add managed search or answers over your content | AI Search | Use a managed retrieval-augmented generation pipeline | [AI Search](references/ai-search/README.md) |
|
||||
| Build custom semantic search or retrieval | Vectorize + Workers AI | Control embeddings, indexing, and retrieval rather than using a managed pipeline | [Vectorize](references/vectorize/README.md); [Workers AI](references/workers-ai/README.md) |
|
||||
| Observe and control requests to AI providers | AI Gateway | Add inference analytics, caching, and request controls | [AI Gateway](references/ai-gateway/README.md) |
|
||||
| Build stateful agents with tools, scheduling, or chat | Agents SDK | Implement agent behavior on Cloudflare; add Dynamic Workers or Sandbox for the required execution runtime | `agents-sdk` skill; [Agents docs](https://developers.cloudflare.com/agents/) |
|
||||
| Build durable agents with TypeScript hooks | Flue | Use an open agent framework with Cloudflare and Node.js targets | [Flue](https://flueframework.com/); [getting started](https://flueframework.com/docs/guide/getting-started/); [Cloudflare target](https://flueframework.com/docs/guide/cloudflare-target/) |
|
||||
| Expose tools through a remote MCP server | Workers + Agents SDK | Publish tools for MCP clients, with authentication appropriate to the service | `agents-sdk` skill, its `references/mcp.md`; [MCP docs](https://developers.cloudflare.com/agents/model-context-protocol/) |
|
||||
| Automate browsers, take screenshots, or extract rendered pages | Browser Run | The task requires a browser rather than a plain HTTP request | [Browser Run](references/browser-rendering/README.md) |
|
||||
| Connect a domain, configure DNS records, or troubleshoot resolution | DNS | Manage authoritative records and choose whether traffic is proxied through Cloudflare | [DNS docs](https://developers.cloudflare.com/dns/) |
|
||||
| Configure HTTPS and certificates | SSL/TLS | Secure connections from visitors to Cloudflare and from Cloudflare to the origin | [SSL/TLS docs](https://developers.cloudflare.com/ssl/) |
|
||||
| Distribute traffic across origins and fail over unhealthy servers | Load Balancing | Use health checks and traffic steering for multiple origin servers | [Load Balancing docs](https://developers.cloudflare.com/load-balancing/) |
|
||||
| Connect an existing server to Cloudflare | Cloudflare Tunnel | Reach an origin without a publicly routable IP address | [Tunnel](references/tunnel/README.md) |
|
||||
| Connect Workers to private services | Workers VPC | Access services in private networks from a Worker | [Workers VPC](references/workers-vpc/README.md) |
|
||||
| Require employee login before accessing an internal app | Access | Put identity-based access policies in front of an internal application | `cloudflare-one` skill; [Access docs](https://developers.cloudflare.com/cloudflare-one/access-controls/) |
|
||||
| Protect access to internal applications and networks | Cloudflare One | Apply identity and network access policies | `cloudflare-one` skill; [Cloudflare One docs](https://developers.cloudflare.com/cloudflare-one/) |
|
||||
| Migrate existing access and network security configurations | Cloudflare One | The task is a supported migration to Cloudflare One | `cloudflare-one-migrations` skill; [Cloudflare One docs](https://developers.cloudflare.com/cloudflare-one/) |
|
||||
| Proxy a TCP or UDP application | Spectrum | Protect and accelerate non-HTTP application traffic | [Spectrum](references/spectrum/README.md) |
|
||||
| Connect a network directly to Cloudflare | Network Interconnect | Dedicated network connectivity is required | [Network Interconnect](references/network-interconnect/README.md) |
|
||||
| Improve routing across the network | Argo Smart Routing | Optimize traffic paths to the origin | [Argo Smart Routing](references/argo-smart-routing/README.md) |
|
||||
| Reduce Worker-to-backend latency | Smart Placement | Place Worker execution closer to the backends it calls | [Smart Placement](references/smart-placement/README.md) |
|
||||
| Redirect URLs, rewrite paths or headers, or change origin routing | Rules | Use Redirect, Transform, or Origin Rules when configuration can express the required behavior | [Rules docs](https://developers.cloudflare.com/rules/) |
|
||||
| Make small HTTP request or response changes | Snippets | Lightweight edge logic meets the need | [Snippets](references/snippets/README.md) |
|
||||
| Protect forms from automated abuse | Turnstile | Add bot challenges and server-side token validation | `turnstile-spin` skill; [Turnstile docs](https://developers.cloudflare.com/turnstile/) |
|
||||
| Filter malicious web requests | WAF | Apply application-layer rules and managed protections | [WAF](references/waf/README.md) |
|
||||
| Protect services from denial-of-service attacks | DDoS Protection | Mitigate attacks at the relevant network or application layer | [DDoS protection](references/ddos/README.md) |
|
||||
| Detect and control automated traffic | Bot Management | Make request decisions based on bot detection | [Bot Management](references/bot-management/README.md) |
|
||||
| Discover and protect API endpoints | API Shield | Apply API-specific protections and validation | [API Shield](references/api-shield/README.md) |
|
||||
| Queue visitors during traffic spikes | Waiting Room | Control admission when application capacity is limited | [Waiting Room docs](https://developers.cloudflare.com/waiting-room/) |
|
||||
| Store a Worker's API keys and credentials | Workers secrets | Bind secrets to a Worker without committing values to source | `wrangler` skill; [secrets docs](https://developers.cloudflare.com/workers/configuration/secrets/) |
|
||||
| Share managed secrets across services | Secrets Store | Manage reusable account-level secrets | [Secrets Store](references/secrets-store/README.md) |
|
||||
| Control where data is processed and stored | Data Localization Suite | Evaluate regional processing and storage controls against the actual requirements | [Data Localization docs](https://developers.cloudflare.com/data-localization/) |
|
||||
| Prove a claim without identifying or tracking the user | Privacy Pass | Use privacy-preserving tokens in a supported integration | [Privacy Pass docs](https://developers.cloudflare.com/privacy-pass/) |
|
||||
| Store, resize, transform, and deliver images | Cloudflare Images | Use managed image processing and delivery | [Images](references/images/README.md) |
|
||||
| Encode, store, and deliver live or on-demand video | Stream | Use managed video infrastructure | [Stream](references/stream/README.md) |
|
||||
| Build an audio/video calling application with SDKs | RealtimeKit | Use application-level SDKs for calls and meetings | [RealtimeKit](references/realtimekit/README.md) |
|
||||
| Build custom real-time media infrastructure | Realtime SFU | Control the application while using a selective forwarding unit for media | [Realtime SFU](references/realtime-sfu/README.md) |
|
||||
| Relay WebRTC connections through restrictive networks | TURN Service | Clients need a connectivity relay | [TURN](references/turn/README.md) |
|
||||
| Deliver live media over QUIC | MoQ | Use the Media over QUIC protocol; check current compatibility and availability | [MoQ docs](https://developers.cloudflare.com/moq/) |
|
||||
| Send transactional email | Email Service | Send application-generated messages | `cloudflare-email-service` skill; [Email Service docs](https://developers.cloudflare.com/email-service/) |
|
||||
| Forward incoming email | Email Routing | Route addresses on a domain to destination mailboxes | [Email Routing](references/email-routing/README.md) |
|
||||
| Process incoming email in code | Email Workers | Apply custom logic to inbound messages | [Email Workers](references/email-workers/README.md) |
|
||||
| Manage third-party tags and scripts | Zaraz | Load and manage third-party tools through Cloudflare | [Zaraz](references/zaraz/README.md) |
|
||||
| Run locally and manage resources from the CLI | Wrangler | Develop, configure, deploy, and inspect the intended account and environment | `wrangler` skill; [Wrangler docs](https://developers.cloudflare.com/workers/wrangler/) |
|
||||
| Test Worker behavior before deployment | Workers testing tools | Choose runtime tests or integration tests for the affected behavior | [Testing docs](https://developers.cloudflare.com/workers/testing/); `durable-objects` skill for DO tests |
|
||||
| Embed local Worker simulation in tooling | Miniflare | A programmatic emulator is needed for a custom development or test harness | [Miniflare](references/miniflare/README.md) |
|
||||
| Run or investigate the underlying Workers runtime | workerd | Work directly with the runtime outside normal managed deployment | [workerd](references/workerd/README.md) |
|
||||
| Try a small Worker in the browser | Workers Playground | Explore or share a minimal example without local setup | [Workers Playground](references/workers-playground/README.md) |
|
||||
| Build and deploy whenever code is pushed | Workers Builds | Connect a Git repository to automated builds and deployments | [Builds docs](https://developers.cloudflare.com/workers/ci-cd/builds/) |
|
||||
| Test a branch or pull request in an isolated environment | Workers Previews | Create a branch environment under the same Worker with its own settings and URLs; check which bound resources are isolated or shared | [Previews docs](https://developers.cloudflare.com/workers/previews/); `wrangler` skill |
|
||||
| Inspect an uploaded version, release it gradually, or roll back code | Workers versions and deployments | Manage application releases that use production resources; rollback does not restore connected resource data | [Deployment docs](https://developers.cloudflare.com/workers/versions-and-deployments/); `wrangler` skill |
|
||||
| Release a feature gradually or target user groups | Flagship | Change feature availability with targeting and percentage rollouts | [Flagship](references/flagship/README.md) |
|
||||
| Manage infrastructure as code | Terraform or Pulumi | Use Terraform for declarative configuration or Pulumi for infrastructure in programming languages | [Terraform](references/terraform/README.md); [Pulumi](references/pulumi/README.md) |
|
||||
| Automate account or product configuration through an API | Cloudflare REST API | Manage resources programmatically; prefer bindings for supported operations inside Workers | [REST API](references/api/README.md) |
|
||||
| Debug failures and trace application requests | Workers Logs and Traces | Investigate runtime errors and execution paths | [Observability](references/observability/README.md) |
|
||||
| Process Worker execution events in code | Tail Workers | Build custom log or exception processing | [Tail Workers](references/tail-workers/README.md) |
|
||||
| Export Worker logs to another system | Workers Logpush | Deliver logs to a supported external destination | [Logpush docs](https://developers.cloudflare.com/workers/observability/logs/logpush/) |
|
||||
| Measure custom application events | Workers Analytics Engine | Analyze high-cardinality event data written from Workers | [Analytics Engine](references/analytics-engine/README.md) |
|
||||
| Measure website usage and visitor performance | Cloudflare Web Analytics | Add website analytics and real-user measurements | [Web Analytics](references/web-analytics/README.md) |
|
||||
| Query metrics across Cloudflare products | GraphQL Analytics API | Retrieve product analytics programmatically | [GraphQL Analytics API](references/graphql-api/README.md) |
|
||||
| Audit page speed and find loading bottlenecks | Web performance tools | Measure and improve the site's actual browser performance | `web-perf` skill; [Web Analytics](references/web-analytics/README.md) |
|
||||
| Ask questions about an account or diagnose its configuration in the dashboard | Agent Lee | Use the dashboard's AI assistant; check current account eligibility | [Agent Lee docs](https://developers.cloudflare.com/agent-lee/) |
|
||||
|
||||
For example, a file-upload app can use Workers for its API, R2 for files, D1 for metadata, and Queues for processing. A document assistant can start with Workers and AI Search; use Vectorize and Workers AI when it needs custom retrieval. Recommend only the pieces the requested behavior needs.
|
||||
|
||||
## Find guidance for a task not listed here
|
||||
|
||||
Use the [Cloudflare product directory](https://developers.cloudflare.com/directory/) for additional products and their current docs. Follow links to the specific feature or API involved. Use [Choose a data or storage product](https://developers.cloudflare.com/workers/platform/storage-options/) for storage tradeoffs, and the product's limits, pricing, and migration guides when evaluating scale, cost, or an upgrade. This table maps common tasks to selected Cloudflare products; it does not enumerate every possible application.
|
||||
|
||||
## Caching
|
||||
|
||||
Prefer [Workers Cache](https://developers.cloudflare.com/workers/cache/) for caching, including [advanced patterns](https://developers.cloudflare.com/workers/cache/examples/) using cached inner entrypoints and programmatic invalidation. Choose [Cache API](https://developers.cloudflare.com/workers/runtime-apis/cache/) or KV caching only when a concrete requirement cannot be met by Workers Cache; check its [patterns](https://developers.cloudflare.com/workers/cache/examples/) and [limitations](https://developers.cloudflare.com/workers/cache/limitations/) first.
|
||||
|
||||
## Working principles
|
||||
|
||||
- Inspect the existing project and its pinned package versions before choosing an API or configuration shape.
|
||||
- Retrieve current Cloudflare documentation when details may have changed. Use installed types and `node_modules/wrangler/config-schema.json` when they represent the project's pinned version.
|
||||
- Preserve the project's architecture and make the smallest change that satisfies the request.
|
||||
- Check current Cloudflare docs before relying on limits, prices, compatibility flags, or security requirements; these can change.
|
||||
- Validate in proportion to the change: use the project's checks, then exercise the affected behavior when practical.
|
||||
|
||||
Cloudflare documentation: <https://developers.cloudflare.com/>
|
||||
Cloudflare changelog: <https://developers.cloudflare.com/changelog/>
|
||||
@@ -0,0 +1,27 @@
|
||||
# Cloudflare AI Gateway
|
||||
|
||||
Use AI Gateway to observe and control requests to AI providers through caching, rate limiting, logging, and routing.
|
||||
|
||||
Fetch the linked documentation before choosing endpoints, authentication headers, SDK options, model names, or limits. Keep implementation details in the current docs.
|
||||
|
||||
## Choose a task
|
||||
|
||||
| Task | Reference |
|
||||
| ------------------------------------------------------------------ | --------------------------------------- |
|
||||
| Create a gateway or choose authentication and provider credentials | [Configuration](./configuration.md) |
|
||||
| Integrate an SDK, direct HTTP, or a Worker binding | [SDK integration](./sdk-integration.md) |
|
||||
| Configure caching, rate limits, security, billing, or logging | [Features](./features.md) |
|
||||
| Add fallbacks, conditional routing, or traffic splits | [Dynamic routing](./dynamic-routing.md) |
|
||||
| Diagnose failed requests, caching, or missing logs | [Troubleshooting](./troubleshooting.md) |
|
||||
|
||||
For new single-model calls, start with the [REST API](https://developers.cloudflare.com/ai-gateway/usage/rest-api/) or [Workers bindings](https://developers.cloudflare.com/ai-gateway/usage/worker-binding-methods/), depending on the runtime. Preserve provider-native integrations when their API shape is needed; use the corresponding [provider guide](https://developers.cloudflare.com/ai-gateway/usage/providers/).
|
||||
|
||||
The [legacy Unified API](https://developers.cloudflare.com/ai-gateway/usage/chat-completion/) is deprecated for single-model calls but remains required for dynamic routes. Check the task before changing an existing endpoint.
|
||||
|
||||
Gateway authentication and upstream provider credentials are separate concerns. Choose the endpoint first, then follow its authentication and billing requirements in [configuration](./configuration.md).
|
||||
|
||||
## Related references
|
||||
|
||||
- [Workers AI](../workers-ai/README.md) — model inference.
|
||||
- [Agents SDK documentation](https://developers.cloudflare.com/agents/) — stateful agents.
|
||||
- [Vectorize](../vectorize/README.md) — vector search.
|
||||
@@ -0,0 +1,18 @@
|
||||
# AI Gateway Configuration
|
||||
|
||||
Choose the request path before configuring authentication: Cloudflare REST inference, a Workers binding, and provider-native gateway endpoints have different requirements. Gateway access does not by itself define which upstream credentials or billing source a request uses.
|
||||
|
||||
| Task | Current documentation |
|
||||
| --------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| Send a first request and locate the account/gateway identifiers | [Getting started](https://developers.cloudflare.com/ai-gateway/get-started/) |
|
||||
| Create, update, or delete a gateway; use the default gateway | [Manage gateways](https://developers.cloudflare.com/ai-gateway/configuration/manage-gateway/) |
|
||||
| Manage gateways programmatically | [Gateway management API](https://developers.cloudflare.com/api/resources/ai_gateway/methods/list/) |
|
||||
| Protect gateway access and choose token permissions for the endpoint | [Authenticated Gateway](https://developers.cloudflare.com/ai-gateway/configuration/authentication/) and [REST API authentication](https://developers.cloudflare.com/ai-gateway/usage/rest-api/#authentication) |
|
||||
| Configure Wrangler and an AI binding | [Workers AI binding setup](https://developers.cloudflare.com/ai-gateway/integrations/aig-workers-ai-binding/) and [binding methods](https://developers.cloudflare.com/ai-gateway/usage/worker-binding-methods/) |
|
||||
| Store provider keys, select aliases, or diagnose missing credentials | [Bring Your Own Keys](https://developers.cloudflare.com/ai-gateway/configuration/bring-your-own-keys/) |
|
||||
| Use Cloudflare billing or determine which credentials take precedence | [Unified Billing](https://developers.cloudflare.com/ai-gateway/features/unified-billing/) |
|
||||
| Supply provider credentials with each request | [Provider guides](https://developers.cloudflare.com/ai-gateway/usage/providers/) |
|
||||
|
||||
Keep credentials out of source code. Use the chosen endpoint's documentation for headers and permissions instead of reusing an authentication recipe from another endpoint. Check credential precedence before changing an existing BYOK or billing setup.
|
||||
|
||||
For SDK selection, see [SDK integration](./sdk-integration.md); for policy settings, see [features](./features.md).
|
||||
@@ -0,0 +1,15 @@
|
||||
# AI Gateway Dynamic Routing
|
||||
|
||||
Use a dynamic route when model selection, traffic splitting, quotas, or fallbacks should be controlled in the gateway. For a simple retry or fallback sequence, check the request-handling and fallback guides before introducing a routing flow.
|
||||
|
||||
| Task | Current documentation |
|
||||
| ------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| Design conditional routes, A/B splits, quotas, model fallbacks, and version rollbacks | [Dynamic routing](https://developers.cloudflare.com/ai-gateway/features/dynamic-routing/) |
|
||||
| Invoke a route from an SDK, HTTP request, or Worker; inspect route response metadata | [Using a dynamic route](https://developers.cloudflare.com/ai-gateway/features/dynamic-routing/usage/) |
|
||||
| Define route elements and connections programmatically | [JSON configuration](https://developers.cloudflare.com/ai-gateway/features/dynamic-routing/json-configuration/) |
|
||||
| Provide metadata used by routing conditions | [Custom metadata](https://developers.cloudflare.com/ai-gateway/observability/custom-metadata/) |
|
||||
| Configure model/provider fallbacks | [Fallbacks](https://developers.cloudflare.com/ai-gateway/configuration/fallbacks/) |
|
||||
| Configure retries, backoff, and timeouts | [Request handling](https://developers.cloudflare.com/ai-gateway/configuration/request-handling/) |
|
||||
| Inspect request outcomes, costs, and errors | [Analytics](https://developers.cloudflare.com/ai-gateway/observability/analytics/) and [logging](https://developers.cloudflare.com/ai-gateway/observability/logging/) |
|
||||
|
||||
Check the usage guide's authentication and stored-key prerequisites. Dynamic routes still use the [Unified API compatibility endpoint](https://developers.cloudflare.com/ai-gateway/usage/chat-completion/); its single-model deprecation does not make the REST inference endpoint a replacement for route invocation.
|
||||
@@ -0,0 +1,22 @@
|
||||
# AI Gateway Features
|
||||
|
||||
Fetch the relevant guide before setting feature flags, headers, limits, or billing behavior.
|
||||
|
||||
| Task | Current documentation |
|
||||
| ------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| Enable caching, set TTLs, bypass the cache, or choose a custom cache key | [Caching](https://developers.cloudflare.com/ai-gateway/features/caching/) |
|
||||
| Control request volume with fixed or sliding limits | [Rate limiting](https://developers.cloudflare.com/ai-gateway/features/rate-limiting/) |
|
||||
| Enforce cost budgets | [Spend limits](https://developers.cloudflare.com/ai-gateway/features/spend-limits/) |
|
||||
| Evaluate and enforce content policies | [Guardrails setup](https://developers.cloudflare.com/ai-gateway/features/guardrails/set-up-guardrail/) and [usage considerations](https://developers.cloudflare.com/ai-gateway/features/guardrails/usage-considerations/) |
|
||||
| Detect sensitive data in prompts and responses | [DLP setup](https://developers.cloudflare.com/ai-gateway/features/dlp/set-up-dlp/) |
|
||||
| Choose provider keys or Cloudflare billing | [BYOK](https://developers.cloudflare.com/ai-gateway/configuration/bring-your-own-keys/) and [Unified Billing](https://developers.cloudflare.com/ai-gateway/features/unified-billing/) |
|
||||
| Configure provider data retention for Unified Billing | [Zero Data Retention](https://developers.cloudflare.com/ai-gateway/features/unified-billing/#zero-data-retention-zdr) |
|
||||
| Configure log collection, payload storage, and retention | [Logging](https://developers.cloudflare.com/ai-gateway/observability/logging/) |
|
||||
| Export logs | [Workers Logpush](https://developers.cloudflare.com/ai-gateway/observability/logging/logpush/) |
|
||||
| Attach request metadata for tracking and routing | [Custom metadata](https://developers.cloudflare.com/ai-gateway/observability/custom-metadata/) |
|
||||
| Override model costs | [Custom costs](https://developers.cloudflare.com/ai-gateway/configuration/custom-costs/) |
|
||||
| Check supported providers, quotas, or pricing | [Provider guides](https://developers.cloudflare.com/ai-gateway/usage/providers/), [limits](https://developers.cloudflare.com/ai-gateway/reference/limits/), and [pricing](https://developers.cloudflare.com/ai-gateway/reference/pricing/) |
|
||||
|
||||
Choose cache keys only for requests whose responses may safely be shared. Decide what prompt and response data may be stored before enabling logging; do not infer a provider's retention policy from gateway log settings.
|
||||
|
||||
For conditional policies and fallbacks, see [dynamic routing](./dynamic-routing.md).
|
||||
@@ -0,0 +1,19 @@
|
||||
# AI Gateway SDK Integration
|
||||
|
||||
Choose an integration that matches the application's runtime and required API shape. Fetch its guide before installing packages or writing requests; model identifiers, SDK options, and gateway headers belong in the docs.
|
||||
|
||||
| Integration task | Current documentation |
|
||||
| ------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| New single-model HTTP calls or OpenAI-compatible clients | [REST API](https://developers.cloudflare.com/ai-gateway/usage/rest-api/) |
|
||||
| Vercel AI SDK, provider adapters, and fallback providers | [Vercel AI SDK integration](https://developers.cloudflare.com/ai-gateway/integrations/vercel-ai-sdk/) |
|
||||
| Preserve OpenAI-native endpoints with the OpenAI SDK, Python, or HTTP | [OpenAI provider guide](https://developers.cloudflare.com/ai-gateway/usage/providers/openai/) |
|
||||
| Preserve Anthropic-native requests | [Anthropic provider guide](https://developers.cloudflare.com/ai-gateway/usage/providers/anthropic/) |
|
||||
| Use another provider or a framework's configurable provider endpoint | [Provider guides](https://developers.cloudflare.com/ai-gateway/usage/providers/) — match the framework's expected API shape to the provider endpoint |
|
||||
| Configure Workers AI and an AI binding | [Binding setup](https://developers.cloudflare.com/ai-gateway/integrations/aig-workers-ai-binding/) |
|
||||
| Call Workers AI or third-party models from a Worker; use gateway methods | [Workers binding methods](https://developers.cloudflare.com/ai-gateway/usage/worker-binding-methods/) |
|
||||
| Set request metadata, caching, or other gateway headers | [Header glossary](https://developers.cloudflare.com/ai-gateway/glossary/) |
|
||||
| Invoke a dynamic route | [Dynamic route usage](https://developers.cloudflare.com/ai-gateway/features/dynamic-routing/usage/) |
|
||||
|
||||
The [legacy Unified API](https://developers.cloudflare.com/ai-gateway/usage/chat-completion/) is deprecated for single-model calls, but dynamic routes still require its compatibility endpoint. Do not migrate a dynamic route to the REST inference endpoint as though it were a single-model call.
|
||||
|
||||
Confirm [gateway authentication and provider credentials](./configuration.md) separately, including the selected path's BYOK and billing behavior.
|
||||
@@ -0,0 +1,16 @@
|
||||
# AI Gateway Troubleshooting
|
||||
|
||||
Identify the request path and whether the failure comes from gateway access, upstream provider authentication, or request policy before changing credentials or retry behavior. A status code alone does not establish the failing layer.
|
||||
|
||||
| Symptom or task | Current documentation |
|
||||
| --------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| Authentication or provider errors, timeouts, DLP failures, or unexpected cache behavior | [Troubleshooting](https://developers.cloudflare.com/ai-gateway/reference/troubleshooting/) |
|
||||
| Gateway authentication failure | [Authenticated Gateway](https://developers.cloudflare.com/ai-gateway/configuration/authentication/) and [REST API authentication](https://developers.cloudflare.com/ai-gateway/usage/rest-api/#authentication) |
|
||||
| Provider key or billing mismatch | [BYOK](https://developers.cloudflare.com/ai-gateway/configuration/bring-your-own-keys/), [Unified Billing](https://developers.cloudflare.com/ai-gateway/features/unified-billing/), and the [provider guide](https://developers.cloudflare.com/ai-gateway/usage/providers/) |
|
||||
| Rate limits or repeated failures | [Rate limiting](https://developers.cloudflare.com/ai-gateway/features/rate-limiting/) and [request handling](https://developers.cloudflare.com/ai-gateway/configuration/request-handling/) |
|
||||
| Unexpected cache hit or miss, including streaming behavior | [Caching](https://developers.cloudflare.com/ai-gateway/features/caching/) |
|
||||
| Missing logs, collection overrides, or storage limits | [Logging](https://developers.cloudflare.com/ai-gateway/observability/logging/) and [limits](https://developers.cloudflare.com/ai-gateway/reference/limits/) |
|
||||
| Inspect headers, request metadata, usage, or export logs | [Header glossary](https://developers.cloudflare.com/ai-gateway/glossary/), [custom metadata](https://developers.cloudflare.com/ai-gateway/observability/custom-metadata/), [analytics](https://developers.cloudflare.com/ai-gateway/observability/analytics/), and [Logpush](https://developers.cloudflare.com/ai-gateway/observability/logging/logpush/) |
|
||||
| Dynamic route failure | [Dynamic route usage](https://developers.cloudflare.com/ai-gateway/features/dynamic-routing/usage/) |
|
||||
|
||||
Check the existing SDK and gateway retry settings together before adding another retry loop. Follow [SDK integration](./sdk-integration.md) when an endpoint or model format is suspect.
|
||||
@@ -0,0 +1,25 @@
|
||||
# Cloudflare AI Search
|
||||
|
||||
Use AI Search for managed content indexing and retrieval, with optional answer generation. Start with [How AI Search works](https://developers.cloudflare.com/ai-search/concepts/how-ai-search-works/).
|
||||
|
||||
## Choose the right product
|
||||
|
||||
- **Managed search or RAG over your content:** AI Search.
|
||||
- **Custom embeddings and vector-index management:** [Vectorize](../vectorize/README.md).
|
||||
- **Model inference without a managed retrieval pipeline:** [Workers AI](../workers-ai/README.md).
|
||||
|
||||
For freshness requirements, read [Syncing](https://developers.cloudflare.com/ai-search/configuration/indexing/syncing/) for your data source before choosing an architecture. Use the current [limits and pricing](https://developers.cloudflare.com/ai-search/platform/limits-pricing/) instead of assuming a fixed indexing interval or account limit.
|
||||
|
||||
## Find the right documentation
|
||||
|
||||
Read the linked page before implementing; these references route to the maintained documentation instead of copying API examples or configuration.
|
||||
|
||||
| Task | Start here |
|
||||
| ---------------------------------------------------------- | ----------------------------------------------------------------------------------------------- |
|
||||
| Build a new Worker integration | [Workers binding quick start](https://developers.cloudflare.com/ai-search/get-started/workers/) |
|
||||
| Choose an API or maintain an existing integration | [API routes](api.md) |
|
||||
| Connect data, configure indexing, or manage environments | [Configuration routes](configuration.md) |
|
||||
| Choose retrieval, generation, or tenant isolation patterns | [Pattern routes](patterns.md) |
|
||||
| Diagnose indexing, authentication, filters, or limits | [Troubleshooting routes](gotchas.md) |
|
||||
|
||||
Existing `env.AI.autorag()` integrations can continue to work. Use the [migration guide](https://developers.cloudflare.com/ai-search/api/migration/workers-binding/) when upgrading; migration is not required just to maintain an existing integration.
|
||||
@@ -0,0 +1,13 @@
|
||||
# AI Search API Routes
|
||||
|
||||
Choose documentation that matches the integration you are working on. Fetch the reference before writing binding configuration, request types, response parsing, or streaming code.
|
||||
|
||||
| Task | Documentation |
|
||||
| ------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------- |
|
||||
| New Worker integration: bindings, search, chat completions, and streaming | [Search Workers binding](https://developers.cloudflare.com/ai-search/api/search/workers-binding/) |
|
||||
| Create, list, configure, and inspect instances | [Instances Workers binding](https://developers.cloudflare.com/ai-search/api/instances/workers-binding/) |
|
||||
| Query over HTTP and configure request authentication | [Search REST API](https://developers.cloudflare.com/ai-search/api/search/rest-api/) |
|
||||
| Maintain an existing `env.AI.autorag()` integration | [Legacy Workers binding](https://developers.cloudflare.com/ai-search/api/migration/workers-binding-legacy/) |
|
||||
| Upgrade a legacy binding, including responses, streaming, and filters | [Workers binding migration](https://developers.cloudflare.com/ai-search/api/migration/workers-binding/) |
|
||||
|
||||
The legacy binding remains supported; use current bindings for new integrations. Keep legacy request and response handling together until deliberately migrating them.
|
||||
@@ -0,0 +1,16 @@
|
||||
# AI Search Configuration Routes
|
||||
|
||||
| Task | Documentation |
|
||||
| ------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------- |
|
||||
| Set up a Worker and local development | [Workers binding quick start](https://developers.cloudflare.com/ai-search/get-started/workers/) |
|
||||
| Select built-in uploads, R2, or a website; check supported formats | [Data sources](https://developers.cloudflare.com/ai-search/configuration/data-source/) |
|
||||
| Connect existing R2 content | [R2 data source](https://developers.cloudflare.com/ai-search/configuration/data-source/r2/) |
|
||||
| Configure crawling and diagnose bot-protection requirements | [Website data source](https://developers.cloudflare.com/ai-search/configuration/data-source/website/) |
|
||||
| Include or exclude files and URL paths | [Path filtering](https://developers.cloudflare.com/ai-search/configuration/indexing/path-filtering/) |
|
||||
| Configure source syncs, trigger indexing, or pause and resume | [Syncing](https://developers.cloudflare.com/ai-search/configuration/indexing/syncing/) |
|
||||
| Grant AI Search access to R2 for indexing | [Service API token](https://developers.cloudflare.com/ai-search/configuration/indexing/service-api-token/) |
|
||||
| Organize instances by application, tenant, or environment | [Namespaces](https://developers.cloudflare.com/ai-search/concepts/namespaces/) |
|
||||
| Configure models | [Models](https://developers.cloudflare.com/ai-search/configuration/models/) |
|
||||
| Inspect instance configuration and indexing progress | [Instances Workers binding](https://developers.cloudflare.com/ai-search/api/instances/workers-binding/) |
|
||||
|
||||
Indexing credentials and request authentication serve different purposes. For authenticating search requests over HTTP, use the [Search REST API](https://developers.cloudflare.com/ai-search/api/search/rest-api/) documentation.
|
||||
@@ -0,0 +1,13 @@
|
||||
# AI Search Troubleshooting Routes
|
||||
|
||||
| Symptom or question | Documentation |
|
||||
| ------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| API request fails, including authentication or missing instances | [API error codes](https://developers.cloudflare.com/ai-search/troubleshooting/api-error-codes/) |
|
||||
| Upload or sync succeeds but content fails during processing | [Indexing error codes](https://developers.cloudflare.com/ai-search/troubleshooting/indexing-error-codes/) |
|
||||
| Content is missing or stale | [Syncing](https://developers.cloudflare.com/ai-search/configuration/indexing/syncing/) and [supported data sources and formats](https://developers.cloudflare.com/ai-search/configuration/data-source/) |
|
||||
| Filters return unexpected documents or no matches | [Filtering](https://developers.cloudflare.com/ai-search/configuration/retrieval/filtering/) and [metadata attributes](https://developers.cloudflare.com/ai-search/configuration/indexing/metadata/) |
|
||||
| Thresholds exclude results or responses need tuning | [Result controls](https://developers.cloudflare.com/ai-search/configuration/retrieval/result-controls/) |
|
||||
| Binding types, response parsing, or streaming fail after an upgrade | [Workers binding migration](https://developers.cloudflare.com/ai-search/api/migration/workers-binding/) |
|
||||
| Capacity, file-size, or billing questions | [Limits and pricing](https://developers.cloudflare.com/ai-search/platform/limits-pricing/) |
|
||||
|
||||
For legacy binding behavior, start with [API routes](api.md). Do not apply current filter syntax or response shapes to legacy calls without following the migration guide.
|
||||
@@ -0,0 +1,15 @@
|
||||
# AI Search Pattern Routes
|
||||
|
||||
Choose retrieval-only search when your application displays chunks or handles generation itself; choose chat completions when AI Search should also generate the answer. Read [Search Workers binding](https://developers.cloudflare.com/ai-search/api/search/workers-binding/) for both paths and streaming behavior.
|
||||
|
||||
| Task | Documentation |
|
||||
| ------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------- |
|
||||
| Isolate tenants using separate instances or a shared filtered instance | [Multitenancy](https://developers.cloudflare.com/ai-search/how-to/per-tenant-search/) |
|
||||
| Define built-in or custom metadata | [Metadata attributes](https://developers.cloudflare.com/ai-search/configuration/indexing/metadata/) |
|
||||
| Filter by metadata, combine conditions, or match a folder and subfolders | [Filtering](https://developers.cloudflare.com/ai-search/configuration/retrieval/filtering/) |
|
||||
| Tune result count and relevance thresholds | [Result controls](https://developers.cloudflare.com/ai-search/configuration/retrieval/result-controls/) |
|
||||
| Resolve follow-up queries using conversation context | [Query rewriting](https://developers.cloudflare.com/ai-search/configuration/retrieval/query-rewriting/) |
|
||||
| Improve result ordering with a second model | [Reranking](https://developers.cloudflare.com/ai-search/configuration/retrieval/reranking/) |
|
||||
| Customize generation and query-rewriting instructions | [System prompt](https://developers.cloudflare.com/ai-search/configuration/retrieval/system-prompt/) |
|
||||
|
||||
For tenant isolation, read the full multitenancy guide before choosing an approach. A lower-bound folder comparison alone does not establish a tenant boundary; use the documented filtering semantics.
|
||||
@@ -0,0 +1,96 @@
|
||||
# Cloudflare Workers Analytics Engine Reference
|
||||
|
||||
Expert guidance for implementing unlimited-cardinality analytics at scale using Cloudflare Workers Analytics Engine.
|
||||
|
||||
## What is Analytics Engine?
|
||||
|
||||
Time-series analytics database designed for high-cardinality data (millions of unique dimensions). Write data points from Workers, query via SQL API. Use for:
|
||||
|
||||
- Custom user-facing analytics dashboards
|
||||
- Usage-based billing & metering
|
||||
- Per-customer/per-feature monitoring
|
||||
- High-frequency instrumentation without performance impact
|
||||
|
||||
**Key Capability:** Track metrics with unlimited unique values (e.g., millions of user IDs, API keys) without performance degradation.
|
||||
|
||||
## Core Concepts
|
||||
|
||||
| Concept | Description | Example |
|
||||
| -------------- | ------------------------------------ | --------------------------------- |
|
||||
| **Dataset** | Logical table for related metrics | `api_requests`, `user_events` |
|
||||
| **Data Point** | Single measurement with timestamp | One API request's metrics |
|
||||
| **Blobs** | String dimensions (max 20) | endpoint, method, status, user_id |
|
||||
| **Doubles** | Numeric values (max 20) | latency_ms, request_count, bytes |
|
||||
| **Indexes** | Filtered blobs for efficient queries | customer_id, api_key |
|
||||
|
||||
## Reading Order
|
||||
|
||||
| Task | Start Here | Then Read |
|
||||
| -------------------- | ------------------------------------------------------------------------------------ | --------- |
|
||||
| **First-time setup** | [configuration.md](configuration.md) → [api.md](api.md) → [patterns.md](patterns.md) | |
|
||||
| **Writing data** | [api.md](api.md) → [gotchas.md](gotchas.md) (sampling) | |
|
||||
| **Querying data** | [api.md](api.md) (SQL API) → [patterns.md](patterns.md) (examples) | |
|
||||
| **Debugging** | [gotchas.md](gotchas.md) → [api.md](api.md) (limits) | |
|
||||
| **Optimization** | [patterns.md](patterns.md) (anti-patterns) → [gotchas.md](gotchas.md) | |
|
||||
|
||||
## When to Use Analytics Engine
|
||||
|
||||
```
|
||||
Need to track metrics? → Yes
|
||||
↓
|
||||
Millions of unique dimension values? → Yes
|
||||
↓
|
||||
Need real-time queries? → Yes
|
||||
↓
|
||||
Use Analytics Engine ✓
|
||||
|
||||
Alternative scenarios:
|
||||
- Low cardinality (<10k unique values) → Workers Analytics (free tier)
|
||||
- Complex joins/relations → D1 Database
|
||||
- Logs/debugging → Tail Workers (logpush)
|
||||
- External tools → Send to external analytics (Datadog, etc.)
|
||||
```
|
||||
|
||||
## Quick Start
|
||||
|
||||
1. Add binding to `wrangler.jsonc`:
|
||||
|
||||
```jsonc
|
||||
{
|
||||
"analytics_engine_datasets": [{ "binding": "ANALYTICS", "dataset": "my_events" }]
|
||||
}
|
||||
```
|
||||
|
||||
2. Write data points (fire-and-forget, no await):
|
||||
|
||||
```typescript
|
||||
env.ANALYTICS.writeDataPoint({
|
||||
blobs: ['/api/users', 'GET', '200'],
|
||||
doubles: [145.2, 1], // latency_ms, count
|
||||
indexes: [customerId]
|
||||
});
|
||||
```
|
||||
|
||||
3. Query via SQL API (HTTP):
|
||||
|
||||
```sql
|
||||
SELECT blob1, SUM(double2) AS total_requests
|
||||
FROM my_events
|
||||
WHERE index1 = 'customer_123'
|
||||
AND timestamp >= NOW() - INTERVAL '7' DAY
|
||||
GROUP BY blob1
|
||||
ORDER BY total_requests DESC
|
||||
```
|
||||
|
||||
## In This Reference
|
||||
|
||||
- **[configuration.md](configuration.md)** - Setup, bindings, TypeScript types, limits
|
||||
- **[api.md](api.md)** - `writeDataPoint()`, SQL API, query syntax
|
||||
- **[patterns.md](patterns.md)** - Use cases, examples, anti-patterns
|
||||
- **[gotchas.md](gotchas.md)** - Sampling, index selection, troubleshooting
|
||||
|
||||
## See Also
|
||||
|
||||
- [Cloudflare Analytics Engine Docs](https://developers.cloudflare.com/analytics/analytics-engine/)
|
||||
- [GraphQL Analytics API Reference](../graphql-api/) - Query built-in Cloudflare analytics (HTTP, Workers, DNS, Firewall, etc.)
|
||||
- [Observability Reference](../observability/) - Workers Logs, Traces, and real-time debugging
|
||||
@@ -0,0 +1,112 @@
|
||||
# Analytics Engine API Reference
|
||||
|
||||
## Writing Data
|
||||
|
||||
### `writeDataPoint()`
|
||||
|
||||
Fire-and-forget (returns `void`, not Promise). Writes happen asynchronously.
|
||||
|
||||
```typescript
|
||||
interface AnalyticsEngineDataPoint {
|
||||
blobs?: string[]; // Up to 20 strings (dimensions), 16KB each
|
||||
doubles?: number[]; // Up to 20 numbers (metrics)
|
||||
indexes?: string[]; // 1 indexed string for high-cardinality filtering
|
||||
}
|
||||
|
||||
env.ANALYTICS.writeDataPoint({
|
||||
blobs: ['/api/users', 'GET', '200'],
|
||||
doubles: [145.2, 1], // latency_ms, count
|
||||
indexes: ['customer_abc123']
|
||||
});
|
||||
```
|
||||
|
||||
**Behaviors:** No await needed, no error thrown (check tail logs), auto-sampled at high volumes, auto-timestamped.
|
||||
|
||||
**Blob vs Index:** Blob for GROUP BY (<100k unique), Index for filter-only (millions unique).
|
||||
|
||||
### Full Example
|
||||
|
||||
```typescript
|
||||
export default {
|
||||
async fetch(request: Request, env: Env): Promise<Response> {
|
||||
const start = Date.now();
|
||||
const url = new URL(request.url);
|
||||
try {
|
||||
const response = await handleRequest(request);
|
||||
env.ANALYTICS.writeDataPoint({
|
||||
blobs: [url.pathname, request.method, response.status.toString()],
|
||||
doubles: [Date.now() - start, 1],
|
||||
indexes: [request.headers.get('x-api-key') || 'anonymous']
|
||||
});
|
||||
return response;
|
||||
} catch (error) {
|
||||
env.ANALYTICS.writeDataPoint({
|
||||
blobs: [url.pathname, request.method, '500'],
|
||||
doubles: [Date.now() - start, 1, 0]
|
||||
});
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
## SQL API (External Only)
|
||||
|
||||
```bash
|
||||
curl -X POST https://api.cloudflare.com/client/v4/accounts/{account_id}/analytics_engine/sql \
|
||||
-H "Authorization: Bearer $TOKEN" \
|
||||
-d "SELECT blob1 AS endpoint, COUNT(*) AS requests FROM dataset WHERE timestamp >= NOW() - INTERVAL '1' HOUR GROUP BY blob1"
|
||||
```
|
||||
|
||||
### Column References
|
||||
|
||||
```sql
|
||||
-- blob1..blob20, double1..double20, index1, timestamp
|
||||
SELECT blob1 AS endpoint, SUM(double1) AS latency, COUNT(*) AS requests
|
||||
FROM my_dataset
|
||||
WHERE index1 = 'customer_123' AND timestamp >= NOW() - INTERVAL '7' DAY
|
||||
GROUP BY blob1
|
||||
HAVING COUNT(*) > 100
|
||||
ORDER BY requests DESC LIMIT 100
|
||||
```
|
||||
|
||||
**Aggregations:** `SUM()`, `AVG()`, `COUNT()`, `MIN()`, `MAX()`, `quantile(0.95)()`
|
||||
|
||||
**Time ranges:** `NOW() - INTERVAL '1' HOUR`, `BETWEEN '2026-01-01' AND '2026-01-31'`
|
||||
|
||||
### Query Examples
|
||||
|
||||
```sql
|
||||
-- Top endpoints
|
||||
SELECT blob1, COUNT(*) AS requests, AVG(double1) AS avg_latency
|
||||
FROM api_requests WHERE timestamp >= NOW() - INTERVAL '24' HOUR
|
||||
GROUP BY blob1 ORDER BY requests DESC LIMIT 20
|
||||
|
||||
-- Error rate
|
||||
SELECT blob1, COUNT(*) AS total,
|
||||
SUM(if(blob3 LIKE '5%', 1, 0)) AS errors
|
||||
FROM api_requests WHERE timestamp >= NOW() - INTERVAL '1' HOUR
|
||||
GROUP BY blob1 HAVING total > 50
|
||||
|
||||
-- P95 latency
|
||||
SELECT blob1, quantile(0.95)(double1) AS p95
|
||||
FROM api_requests GROUP BY blob1
|
||||
```
|
||||
|
||||
## Response Format
|
||||
|
||||
```json
|
||||
{ "data": [{ "endpoint": "/api/users", "requests": 1523 }], "rows": 2 }
|
||||
```
|
||||
|
||||
## Limits
|
||||
|
||||
| Resource | Limit |
|
||||
| ----------------------- | ------- |
|
||||
| Blobs/Doubles per point | 20 each |
|
||||
| Indexes per point | 1 |
|
||||
| Blob/Index size | 16KB |
|
||||
| Data retention | 90 days |
|
||||
| Query timeout | 30s |
|
||||
|
||||
**Critical:** High write volumes (>1M/min) trigger automatic sampling.
|
||||
+107
@@ -0,0 +1,107 @@
|
||||
# Analytics Engine Configuration
|
||||
|
||||
## Setup
|
||||
|
||||
1. Add binding to `wrangler.jsonc`
|
||||
2. Deploy Worker
|
||||
3. Dataset created automatically on first write
|
||||
4. Query via SQL API
|
||||
|
||||
## wrangler.jsonc
|
||||
|
||||
```jsonc
|
||||
{
|
||||
"name": "my-worker",
|
||||
"analytics_engine_datasets": [{ "binding": "ANALYTICS", "dataset": "my_events" }]
|
||||
}
|
||||
```
|
||||
|
||||
Multiple datasets for separate concerns:
|
||||
|
||||
```jsonc
|
||||
{
|
||||
"analytics_engine_datasets": [
|
||||
{ "binding": "API_ANALYTICS", "dataset": "api_requests" },
|
||||
{ "binding": "USER_EVENTS", "dataset": "user_activity" }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## TypeScript
|
||||
|
||||
```typescript
|
||||
interface Env {
|
||||
ANALYTICS: AnalyticsEngineDataset;
|
||||
}
|
||||
|
||||
export default {
|
||||
async fetch(request: Request, env: Env) {
|
||||
// No await - returns void, fire-and-forget
|
||||
env.ANALYTICS.writeDataPoint({
|
||||
blobs: [pathname, method, status], // String dimensions (max 20)
|
||||
doubles: [latency, 1], // Numeric metrics (max 20)
|
||||
indexes: [apiKey] // High-cardinality filter (max 1)
|
||||
});
|
||||
return response;
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
## Data Point Limits
|
||||
|
||||
| Field | Limit | SQL Access |
|
||||
| ------- | --------------------- | ---------------------- |
|
||||
| blobs | 20 strings, 16KB each | `blob1`...`blob20` |
|
||||
| doubles | 20 numbers | `double1`...`double20` |
|
||||
| indexes | 1 string, 16KB | `index1` |
|
||||
|
||||
## Write Behavior
|
||||
|
||||
| Scenario | Behavior |
|
||||
| -------------- | -------------------------------- |
|
||||
| <1M writes/min | All accepted |
|
||||
| >1M writes/min | Automatic sampling |
|
||||
| Invalid data | Silent failure (check tail logs) |
|
||||
|
||||
**Mitigate sampling:** Pre-aggregate, use multiple datasets, write only critical metrics.
|
||||
|
||||
## Query Limits
|
||||
|
||||
| Resource | Limit |
|
||||
| -------------- | ----------------- |
|
||||
| Query timeout | 30 seconds |
|
||||
| Data retention | 90 days (default) |
|
||||
| Result size | ~10MB |
|
||||
|
||||
## Cost
|
||||
|
||||
**Free tier:** 10M writes/month, 1M reads/month
|
||||
|
||||
**Paid:** $0.05 per 1M writes, $1.00 per 1M reads
|
||||
|
||||
## Environment-Specific
|
||||
|
||||
```jsonc
|
||||
{
|
||||
"analytics_engine_datasets": [{ "binding": "ANALYTICS", "dataset": "prod_events" }],
|
||||
"env": {
|
||||
"staging": {
|
||||
"analytics_engine_datasets": [{ "binding": "ANALYTICS", "dataset": "staging_events" }]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Monitoring
|
||||
|
||||
```bash
|
||||
npx wrangler tail # Check for sampling/write errors
|
||||
```
|
||||
|
||||
```sql
|
||||
-- Check write activity
|
||||
SELECT DATE_TRUNC('hour', timestamp) AS hour, COUNT(*) AS writes
|
||||
FROM my_dataset
|
||||
WHERE timestamp >= NOW() - INTERVAL '24' HOUR
|
||||
GROUP BY hour
|
||||
```
|
||||
@@ -0,0 +1,87 @@
|
||||
# Analytics Engine Gotchas
|
||||
|
||||
## Critical Issues
|
||||
|
||||
### Sampling at High Volumes
|
||||
|
||||
**Problem:** Queries return fewer points than written at >1M writes/min.
|
||||
|
||||
**Solution:**
|
||||
|
||||
```typescript
|
||||
// Pre-aggregate before writing
|
||||
let buffer = { count: 0, total: 0 };
|
||||
buffer.count++;
|
||||
buffer.total += value;
|
||||
|
||||
// Write once per second instead of per request
|
||||
if (Date.now() % 1000 === 0) {
|
||||
env.ANALYTICS.writeDataPoint({ doubles: [buffer.count, buffer.total] });
|
||||
}
|
||||
```
|
||||
|
||||
**Detection:** `npx wrangler tail` → look for "sampling enabled"
|
||||
|
||||
### writeDataPoint Returns void
|
||||
|
||||
```typescript
|
||||
// ❌ Pointless await
|
||||
await env.ANALYTICS.writeDataPoint({...});
|
||||
|
||||
// ✅ Fire-and-forget
|
||||
env.ANALYTICS.writeDataPoint({...});
|
||||
```
|
||||
|
||||
Writes can fail silently. Check tail logs.
|
||||
|
||||
### Index vs Blob
|
||||
|
||||
| Cardinality | Use | Example |
|
||||
| ----------- | --------- | ------------------------------ |
|
||||
| Millions | **Index** | user_id, api_key |
|
||||
| Hundreds | **Blob** | endpoint, status_code, country |
|
||||
|
||||
```typescript
|
||||
// ✅ Correct
|
||||
{ blobs: [method, path, status], indexes: [userId] }
|
||||
```
|
||||
|
||||
### Can't Query from Workers
|
||||
|
||||
Query API requires HTTP auth. Use external service or cache in KV/D1.
|
||||
|
||||
### No Custom Timestamps
|
||||
|
||||
Auto-generated at write time. Store original in blob if needed.
|
||||
|
||||
## Common Errors
|
||||
|
||||
| Error | Fix |
|
||||
| ----------------- | ---------------------------------------------- |
|
||||
| Binding not found | Check wrangler.jsonc, redeploy |
|
||||
| No data in query | Wait 30s; check dataset name; check time range |
|
||||
| Query timeout | Add time filter; use index for filtering |
|
||||
|
||||
## Limits
|
||||
|
||||
| Resource | Limit |
|
||||
| ------------------------ | ------- |
|
||||
| Blobs per point | 20 |
|
||||
| Doubles per point | 20 |
|
||||
| Indexes per point | 1 |
|
||||
| Blob/Index size | 16KB |
|
||||
| Write rate (no sampling) | ~1M/min |
|
||||
| Retention | 90 days |
|
||||
| Query timeout | 30s |
|
||||
|
||||
## Best Practices
|
||||
|
||||
✅ Pre-aggregate at high volumes
|
||||
✅ Use index for high-cardinality (millions)
|
||||
✅ Always include time filter in queries
|
||||
✅ Design schema before coding
|
||||
|
||||
❌ Don't await writeDataPoint
|
||||
❌ Don't use index for low-cardinality
|
||||
❌ Don't query without time range
|
||||
❌ Don't assume all writes succeed
|
||||
@@ -0,0 +1,83 @@
|
||||
# Analytics Engine Patterns
|
||||
|
||||
## Use Cases
|
||||
|
||||
| Use Case | Key Metrics | Index On |
|
||||
| -------------- | ------------------------------ | ----------- |
|
||||
| API Metering | requests, bytes, compute_units | api_key |
|
||||
| Feature Usage | feature, action, duration | user_id |
|
||||
| Error Tracking | error_type, endpoint, count | customer_id |
|
||||
| Performance | latency_ms, cache_status | endpoint |
|
||||
| A/B Testing | variant, conversions | user_id |
|
||||
|
||||
## API Metering (Billing)
|
||||
|
||||
```typescript
|
||||
env.ANALYTICS.writeDataPoint({
|
||||
blobs: [pathname, method, status, tier],
|
||||
doubles: [1, computeUnits, bytes, latencyMs],
|
||||
indexes: [apiKey]
|
||||
});
|
||||
|
||||
// Query: Monthly usage by customer
|
||||
// SELECT index1 AS api_key, SUM(double2) AS compute_units
|
||||
// FROM usage WHERE timestamp >= DATE_TRUNC('month', NOW()) GROUP BY index1
|
||||
```
|
||||
|
||||
## Error Tracking
|
||||
|
||||
```typescript
|
||||
env.ANALYTICS.writeDataPoint({
|
||||
blobs: [endpoint, method, errorName, errorMessage.slice(0, 1000)],
|
||||
doubles: [1, timeToErrorMs],
|
||||
indexes: [customerId]
|
||||
});
|
||||
```
|
||||
|
||||
## Performance Monitoring
|
||||
|
||||
```typescript
|
||||
env.ANALYTICS.writeDataPoint({
|
||||
blobs: [pathname, method, cacheStatus, status],
|
||||
doubles: [latencyMs, 1],
|
||||
indexes: [userId]
|
||||
});
|
||||
|
||||
// Query: P95 latency by endpoint
|
||||
// SELECT blob1, quantile(0.95)(double1) AS p95_ms FROM perf GROUP BY blob1
|
||||
```
|
||||
|
||||
## Anti-Patterns
|
||||
|
||||
| ❌ Wrong | ✅ Correct |
|
||||
| ------------------------------------- | -------------------------------------- |
|
||||
| `await writeDataPoint()` | `writeDataPoint()` (fire-and-forget) |
|
||||
| `indexes: [method]` (low cardinality) | `blobs: [method]`, `indexes: [userId]` |
|
||||
| `blobs: [JSON.stringify(obj)]` | Store ID in blob, full object in D1/KV |
|
||||
| Write every request at 10M/min | Pre-aggregate per second |
|
||||
| Query from Worker | Query from external service/API |
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Design schema upfront** - Document blob/double/index assignments
|
||||
2. **Always include count metric** - `doubles: [latency, 1]` for AVG calculations
|
||||
3. **Use enums for blobs** - Consistent values like `Status.SUCCESS`
|
||||
4. **Handle sampling** - Use ratios (avg_latency = SUM(latency)/SUM(count))
|
||||
5. **Test queries early** - Validate schema before heavy writes
|
||||
|
||||
## Schema Template
|
||||
|
||||
```typescript
|
||||
/**
|
||||
* Dataset: my_metrics
|
||||
*
|
||||
* Blobs:
|
||||
* blob1: endpoint, blob2: method, blob3: status
|
||||
*
|
||||
* Doubles:
|
||||
* double1: latency_ms, double2: count (always 1)
|
||||
*
|
||||
* Indexes:
|
||||
* index1: customer_id (high cardinality)
|
||||
*/
|
||||
```
|
||||
@@ -0,0 +1,44 @@
|
||||
# Cloudflare API Shield Reference
|
||||
|
||||
Expert guidance for API Shield - comprehensive API security suite for discovery, protection, and monitoring.
|
||||
|
||||
## Reading Order
|
||||
|
||||
| Task | Files to Read |
|
||||
| ------------------------ | ------------------------------ |
|
||||
| Initial setup | README → configuration.md |
|
||||
| Implement JWT validation | configuration.md → api.md |
|
||||
| Add schema validation | configuration.md → patterns.md |
|
||||
| Detect API attacks | patterns.md → api.md |
|
||||
| Debug issues | gotchas.md |
|
||||
|
||||
## Feature Selection
|
||||
|
||||
What protection do you need?
|
||||
|
||||
```
|
||||
├─ Validate request/response structure → Schema Validation 2.0 (configuration.md)
|
||||
├─ Verify auth tokens → JWT Validation (configuration.md)
|
||||
├─ Client certificates → mTLS (configuration.md)
|
||||
├─ Detect BOLA attacks → BOLA Detection (patterns.md)
|
||||
├─ Track auth coverage → Auth Posture (patterns.md)
|
||||
├─ Stop volumetric abuse → Abuse Detection (patterns.md)
|
||||
└─ Discover shadow APIs → API Discovery (api.md)
|
||||
```
|
||||
|
||||
## In This Reference
|
||||
|
||||
- **[configuration.md](configuration.md)** - Setup, session identifiers, rules, token/mTLS configs
|
||||
- **[api.md](api.md)** - Endpoint management, discovery, validation APIs, GraphQL operations
|
||||
- **[patterns.md](patterns.md)** - Common patterns, progressive rollout, OWASP mappings, workflows
|
||||
- **[gotchas.md](gotchas.md)** - Troubleshooting, false positives, performance, best practices
|
||||
|
||||
## Quick Start
|
||||
|
||||
API Shield: Enterprise-grade API security (Discovery, Schema Validation 2.0, JWT, mTLS, BOLA Detection, Auth Posture). Available as Enterprise add-on with preview access.
|
||||
|
||||
## See Also
|
||||
|
||||
- [API Shield Docs](https://developers.cloudflare.com/api-shield/)
|
||||
- [API Reference](https://developers.cloudflare.com/api/resources/api_gateway/)
|
||||
- [OWASP API Security Top 10](https://owasp.org/www-project-api-security/)
|
||||
@@ -0,0 +1,153 @@
|
||||
# API Reference
|
||||
|
||||
Base: `/zones/{zone_id}/api_gateway`
|
||||
|
||||
## Endpoints
|
||||
|
||||
```bash
|
||||
GET /operations # List
|
||||
GET /operations/{op_id} # Get single
|
||||
POST /operations/item # Create: {endpoint,host,method}
|
||||
POST /operations # Bulk: {operations:[{endpoint,host,method}]}
|
||||
DELETE /operations/{op_id} # Delete
|
||||
DELETE /operations # Bulk delete: {operation_ids:[...]}
|
||||
```
|
||||
|
||||
## Discovery
|
||||
|
||||
```bash
|
||||
GET /discovery/operations # List discovered
|
||||
PATCH /discovery/operations/{op_id} # Update: {state:"saved"|"ignored"}
|
||||
PATCH /discovery/operations # Bulk: {operation_ids:{id:{state}}}
|
||||
GET /discovery # OpenAPI export
|
||||
```
|
||||
|
||||
## Config
|
||||
|
||||
```bash
|
||||
GET /configuration # Get session ID config
|
||||
PUT /configuration # Update: {auth_id_characteristics:[{name,type:"header"|"cookie"}]}
|
||||
```
|
||||
|
||||
## Token Validation
|
||||
|
||||
```bash
|
||||
GET /token_validation # List
|
||||
POST /token_validation # Create: {name,location:{header:"..."},jwks:"..."}
|
||||
POST /jwt_validation_rules # Rule: {name,hostname,token_validation_id,action:"block"}
|
||||
```
|
||||
|
||||
## Workers Integration
|
||||
|
||||
### Access JWT Claims
|
||||
|
||||
```js
|
||||
export default {
|
||||
async fetch(req, env) {
|
||||
// Access validated JWT payload
|
||||
const jwt = req.cf?.jwt?.payload?.[env.JWT_CONFIG_ID]?.[0];
|
||||
if (jwt) {
|
||||
const userId = jwt.sub;
|
||||
const role = jwt.role;
|
||||
}
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
### Access mTLS Info
|
||||
|
||||
```js
|
||||
export default {
|
||||
async fetch(req, env) {
|
||||
const tls = req.cf?.tlsClientAuth;
|
||||
if (tls?.certVerified === 'SUCCESS') {
|
||||
const fingerprint = tls.certFingerprintSHA256;
|
||||
// Authenticated client
|
||||
}
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
### Dynamic JWKS Update
|
||||
|
||||
```js
|
||||
export default {
|
||||
async scheduled(event, env) {
|
||||
const jwks = await (await fetch('https://auth.example.com/.well-known/jwks.json')).json();
|
||||
await fetch(
|
||||
`https://api.cloudflare.com/client/v4/zones/${env.ZONE_ID}/api_gateway/token_validation/${env.CONFIG_ID}`,
|
||||
{
|
||||
method: 'PATCH',
|
||||
headers: {
|
||||
Authorization: `Bearer ${env.CF_API_TOKEN}`,
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({ jwks: JSON.stringify(jwks) })
|
||||
}
|
||||
);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
## Firewall Fields
|
||||
|
||||
### Core Fields
|
||||
|
||||
```js
|
||||
cf.api_gateway.auth_id_present; // Session ID present
|
||||
cf.api_gateway.request_violates_schema; // Schema violation
|
||||
cf.api_gateway.fallthrough_triggered; // No endpoint match
|
||||
cf.tls_client_auth.cert_verified; // mTLS cert valid
|
||||
cf.tls_client_auth.cert_fingerprint_sha256;
|
||||
```
|
||||
|
||||
### JWT Validation (2026)
|
||||
|
||||
```js
|
||||
// Modern validation syntax
|
||||
is_jwt_valid(http.request.jwt.payload['{config_id}'][0]);
|
||||
|
||||
// Legacy (still supported)
|
||||
cf.api_gateway.jwt_claims_valid;
|
||||
|
||||
// Extract claims
|
||||
lookup_json_string(http.request.jwt.payload['{config_id}'][0], 'claim_name');
|
||||
```
|
||||
|
||||
### Risk Labels (2026)
|
||||
|
||||
```js
|
||||
// BOLA detection
|
||||
cf.api_gateway.cf - risk - bola - enumeration; // Sequential resource access detected
|
||||
cf.api_gateway.cf - risk - bola - pollution; // Parameter pollution detected
|
||||
|
||||
// Authentication posture
|
||||
cf.api_gateway.cf - risk - missing - auth; // Endpoint lacks authentication
|
||||
cf.api_gateway.cf - risk - mixed - auth; // Inconsistent auth patterns
|
||||
```
|
||||
|
||||
## BOLA Detection
|
||||
|
||||
```bash
|
||||
GET /user_schemas/{schema_id}/bola # Get BOLA config
|
||||
PATCH /user_schemas/{schema_id}/bola # Update: {enabled:true}
|
||||
```
|
||||
|
||||
## Auth Posture
|
||||
|
||||
```bash
|
||||
GET /discovery/authentication_posture # List unprotected endpoints
|
||||
```
|
||||
|
||||
## GraphQL Protection
|
||||
|
||||
```bash
|
||||
GET /settings/graphql_protection # Get limits
|
||||
PUT /settings/graphql_protection # Set: {max_depth,max_size}
|
||||
```
|
||||
|
||||
## See Also
|
||||
|
||||
- [configuration.md](configuration.md) - Setup guides for all features
|
||||
- [patterns.md](patterns.md) - Firewall rules and common patterns
|
||||
- [API Gateway API Docs](https://developers.cloudflare.com/api/resources/api_gateway/)
|
||||
@@ -0,0 +1,210 @@
|
||||
# Configuration
|
||||
|
||||
## Schema Validation 2.0 Setup
|
||||
|
||||
> ⚠️ **Classic Schema Validation deprecated.** Use Schema Validation 2.0.
|
||||
|
||||
**Upload schema (Dashboard):**
|
||||
|
||||
```
|
||||
Security > API Shield > Schema Validation > Add validation
|
||||
- Upload .yml/.yaml/.json (OpenAPI v3.0)
|
||||
- Endpoints auto-added to Endpoint Management
|
||||
- Action: Log | Block | None
|
||||
- Body inspection: JSON payloads
|
||||
```
|
||||
|
||||
**Change validation action:**
|
||||
|
||||
```
|
||||
Security > API Shield > Settings > Schema Validation
|
||||
Per-endpoint: Filter → ellipses → Change action
|
||||
Default action: Set global mitigation action
|
||||
```
|
||||
|
||||
**Migration from Classic:**
|
||||
|
||||
```
|
||||
1. Export existing schema (if available)
|
||||
2. Delete all Classic schema validation rules
|
||||
3. Wait 5 min for cache clear
|
||||
4. Re-upload via Schema Validation 2.0 interface
|
||||
5. Verify in Security > Events
|
||||
```
|
||||
|
||||
**Fallthrough rule** (catch-all unknown endpoints):
|
||||
|
||||
```
|
||||
Security > API Shield > Settings > Fallthrough > Use Template
|
||||
- Select hostnames
|
||||
- Create rule with cf.api_gateway.fallthrough_triggered
|
||||
- Action: Log (discover) or Block (strict)
|
||||
```
|
||||
|
||||
**Body inspection:** Supports `application/json`, `*/*`, `application/*`. Disable origin MIME sniffing to prevent bypasses.
|
||||
|
||||
## JWT Validation
|
||||
|
||||
**Setup token config:**
|
||||
|
||||
```
|
||||
Security > API Shield > Settings > JWT Settings > Add configuration
|
||||
- Name: "Auth0 JWT Config"
|
||||
- Location: Header/Cookie + name (e.g., "Authorization")
|
||||
- JWKS: Paste public keys from IdP
|
||||
```
|
||||
|
||||
**Create validation rule:**
|
||||
|
||||
```
|
||||
Security > API Shield > API Rules > Add rule
|
||||
- Hostname: api.example.com
|
||||
- Deselect endpoints to ignore
|
||||
- Token config: Select config
|
||||
- Enforce presence: Ignore or Mark as non-compliant
|
||||
- Action: Log/Block/Challenge
|
||||
```
|
||||
|
||||
**Rate limit by JWT claim:**
|
||||
|
||||
```wirefilter
|
||||
lookup_json_string(http.request.jwt.claims["{config_id}"][0], "sub")
|
||||
```
|
||||
|
||||
**Special cases:**
|
||||
|
||||
- Two JWTs, different IdPs: Create 2 configs, select both, "Validate all"
|
||||
- IdP migration: 2 configs + 2 rules, adjust actions per state
|
||||
- Bearer prefix: API Shield handles with/without
|
||||
- Nested claims: Dot notation `user.email`
|
||||
|
||||
## Mutual TLS (mTLS)
|
||||
|
||||
**Setup:**
|
||||
|
||||
```
|
||||
SSL/TLS > Client Certificates > Create Certificate
|
||||
- Generate CF-managed CA (all plans)
|
||||
- Upload custom CA (Enterprise, max 5)
|
||||
```
|
||||
|
||||
**Configure mTLS rule:**
|
||||
|
||||
```
|
||||
Security > API Shield > mTLS
|
||||
- Select hostname(s)
|
||||
- Choose certificate(s)
|
||||
- Action: Block/Log/Challenge
|
||||
```
|
||||
|
||||
**Test:**
|
||||
|
||||
```bash
|
||||
openssl req -x509 -newkey rsa:4096 -keyout client-key.pem -out client-cert.pem -days 365
|
||||
curl https://api.example.com/endpoint --cert client-cert.pem --key client-key.pem
|
||||
```
|
||||
|
||||
## Session Identifiers
|
||||
|
||||
Critical for BOLA Detection, Sequence Mitigation, and analytics. Configure header/cookie that uniquely IDs API users.
|
||||
|
||||
**Examples:** JWT sub claim, session token, API key, custom user ID header
|
||||
|
||||
**Configure:**
|
||||
|
||||
```
|
||||
Security > API Shield > Settings > Session Identifiers
|
||||
- Type: Header/Cookie
|
||||
- Name: "X-User-ID" or "Authorization"
|
||||
```
|
||||
|
||||
## BOLA Detection
|
||||
|
||||
Detects Broken Object Level Authorization attacks (enumeration + parameter pollution).
|
||||
|
||||
**Enable:**
|
||||
|
||||
```
|
||||
Security > API Shield > Schema Validation > [Select Schema] > BOLA Detection
|
||||
- Enable detection
|
||||
- Threshold: Sensitivity level (Low/Medium/High)
|
||||
- Action: Log or Block
|
||||
```
|
||||
|
||||
**Requirements:**
|
||||
|
||||
- Schema Validation 2.0 enabled
|
||||
- Session identifiers configured
|
||||
- Minimum traffic: 1000+ requests/day per endpoint
|
||||
|
||||
## Authentication Posture
|
||||
|
||||
Identifies unprotected or inconsistently protected endpoints.
|
||||
|
||||
**View report:**
|
||||
|
||||
```
|
||||
Security > API Shield > Authentication Posture
|
||||
- Shows endpoints lacking JWT/mTLS
|
||||
- Highlights mixed authentication patterns
|
||||
```
|
||||
|
||||
**Remediate:**
|
||||
|
||||
1. Review flagged endpoints
|
||||
2. Add JWT validation rules
|
||||
3. Configure mTLS for sensitive endpoints
|
||||
4. Monitor posture score
|
||||
|
||||
## Volumetric Abuse + GraphQL
|
||||
|
||||
**Volumetric Abuse Detection:**
|
||||
`Security > API Shield > Settings > Volumetric Abuse Detection`
|
||||
|
||||
- Enable per-endpoint monitoring, set thresholds, action: Log | Challenge | Block
|
||||
|
||||
**GraphQL Protection:**
|
||||
`Security > API Shield > Settings > GraphQL Protection`
|
||||
|
||||
- Max query depth: 10, max size: 100KB, block introspection (production)
|
||||
|
||||
## Terraform
|
||||
|
||||
```hcl
|
||||
# Session identifier
|
||||
resource "cloudflare_api_shield" "main" {
|
||||
zone_id = var.zone_id
|
||||
auth_id_characteristics {
|
||||
type = "header"
|
||||
name = "Authorization"
|
||||
}
|
||||
}
|
||||
|
||||
# Add endpoint
|
||||
resource "cloudflare_api_shield_operation" "users_get" {
|
||||
zone_id = var.zone_id
|
||||
method = "GET"
|
||||
host = "api.example.com"
|
||||
endpoint = "/api/users/{id}"
|
||||
}
|
||||
|
||||
# JWT validation rule
|
||||
resource "cloudflare_ruleset" "jwt_validation" {
|
||||
zone_id = var.zone_id
|
||||
name = "API JWT Validation"
|
||||
kind = "zone"
|
||||
phase = "http_request_firewall_custom"
|
||||
|
||||
rules {
|
||||
action = "block"
|
||||
expression = "(http.host eq \"api.example.com\" and not is_jwt_valid(http.request.jwt.payload[\"{config_id}\"][0]))"
|
||||
description = "Block invalid JWTs"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## See Also
|
||||
|
||||
- [api.md](api.md) - API endpoints and Workers integration
|
||||
- [patterns.md](patterns.md) - Firewall rules and deployment patterns
|
||||
- [gotchas.md](gotchas.md) - Troubleshooting and limits
|
||||
@@ -0,0 +1,132 @@
|
||||
# Gotchas & Troubleshooting
|
||||
|
||||
## Common Errors
|
||||
|
||||
### "Schema Validation 2.0 not working after migration"
|
||||
|
||||
**Cause:** Classic rules still active, conflicting with new system
|
||||
**Solution:**
|
||||
|
||||
1. Delete ALL Classic schema validation rules
|
||||
2. Clear Cloudflare cache (wait 5 min)
|
||||
3. Re-upload schema via new Schema Validation 2.0 interface
|
||||
4. Verify in Security > Events
|
||||
5. Check action is set (Log/Block)
|
||||
|
||||
### "Schema validation blocking valid requests"
|
||||
|
||||
**Cause:** Schema too restrictive, missing fields, or incorrect types
|
||||
**Solution:**
|
||||
|
||||
1. Check Firewall Events for violation details
|
||||
2. Review schema in Settings
|
||||
3. Test schema in Swagger Editor
|
||||
4. Use Log mode to validate before blocking
|
||||
5. Update schema with correct specifications
|
||||
6. Ensure Schema Validation 2.0 (not Classic)
|
||||
|
||||
### "JWT validation failing"
|
||||
|
||||
**Cause:** JWKS mismatch with IdP, expired token, wrong header/cookie name, or clock skew
|
||||
**Solution:**
|
||||
|
||||
1. Verify JWKS matches IdP configuration
|
||||
2. Check token `exp` claim is valid
|
||||
3. Confirm header/cookie name matches config
|
||||
4. Test token at jwt.io
|
||||
5. Account for clock skew (±5 min tolerance)
|
||||
6. Use modern syntax: `is_jwt_valid(http.request.jwt.payload["{config_id}"][0])`
|
||||
|
||||
### "BOLA detection false positives"
|
||||
|
||||
**Cause:** Legitimate sequential access patterns, bulk operations, or sensitivity too high
|
||||
**Solution:**
|
||||
|
||||
1. Review BOLA events in Security > Events
|
||||
2. Lower sensitivity threshold (High → Medium → Low)
|
||||
3. Exclude legitimate bulk operations from detection
|
||||
4. Ensure session identifiers uniquely identify users
|
||||
5. Verify minimum traffic requirements met (1000+ req/day)
|
||||
|
||||
### "Risk labels not appearing in firewall rules"
|
||||
|
||||
**Cause:** Feature not enabled, insufficient traffic, or missing session identifiers
|
||||
**Solution:**
|
||||
|
||||
1. Verify Schema Validation 2.0 enabled
|
||||
2. Enable BOLA Detection in schema settings
|
||||
3. Configure session identifiers (required for BOLA)
|
||||
4. Wait 24-48h for ML model training
|
||||
5. Check minimum traffic thresholds met
|
||||
|
||||
### "Endpoint discovery not finding APIs"
|
||||
|
||||
**Cause:** Insufficient traffic (<500 reqs/10d), non-2xx responses, Worker direct requests, or incorrect session ID config
|
||||
**Solution:** Ensure 500+ requests in 10 days, 2xx responses from edge (not Workers direct), configure session IDs correctly. ML updates daily.
|
||||
|
||||
### "Sequence detection false positives"
|
||||
|
||||
**Cause:** Lookback window issues, non-unique session IDs, or model sensitivity
|
||||
**Solution:**
|
||||
|
||||
1. Review lookback settings (10 reqs to managed endpoints, 10min window)
|
||||
2. Ensure session ID uniqueness per user (not shared tokens)
|
||||
3. Adjust positive/negative model balance
|
||||
4. Exclude legitimate workflows from detection
|
||||
|
||||
### "GraphQL protection blocking valid queries"
|
||||
|
||||
**Cause:** Query depth/size limits too restrictive, complex but legitimate queries
|
||||
**Solution:**
|
||||
|
||||
1. Review blocked query patterns in Security > Events
|
||||
2. Increase max_depth (default: 10) if needed
|
||||
3. Increase max_size (default: 100KB) for complex queries
|
||||
4. Whitelist specific query signatures
|
||||
5. Use Log mode to tune before blocking
|
||||
|
||||
### "Token invalid"
|
||||
|
||||
**Cause:** Configuration error, JWKS mismatch, or expired token
|
||||
**Solution:** Verify config matches IdP, update JWKS, check token expiration
|
||||
|
||||
### "Schema violation"
|
||||
|
||||
**Cause:** Missing required fields, wrong data types, or spec mismatch
|
||||
**Solution:** Review schema against actual requests, ensure all required fields present, validate types match spec
|
||||
|
||||
### "Fallthrough"
|
||||
|
||||
**Cause:** Unknown endpoint or pattern mismatch
|
||||
**Solution:** Update schema with all endpoints, check path pattern matching
|
||||
|
||||
### "mTLS failed"
|
||||
|
||||
**Cause:** Certificate untrusted/expired or wrong CA
|
||||
**Solution:** Verify cert chain, check expiration, confirm correct CA uploaded
|
||||
|
||||
## Limits (2026)
|
||||
|
||||
| Resource/Limit | Value | Notes |
|
||||
| ------------------------- | ----------------------- | ---------------------------------- |
|
||||
| OpenAPI version | v3.0.x only | No external refs, must be valid |
|
||||
| Schema operations | 10K (Enterprise) | Contact for higher limits |
|
||||
| JWT validation sources | Headers/cookies only | No query params/body |
|
||||
| Endpoint discovery | 500+ reqs/10d | Minimum for ML model |
|
||||
| Path normalization | Automatic | `/profile/238` → `/profile/{var1}` |
|
||||
| Schema parameters | No `content` field | No object param validation |
|
||||
| BOLA detection | 1000+ reqs/day/endpoint | Per-endpoint minimum |
|
||||
| Session ID uniqueness | Required | BOLA/Sequence need unique IDs |
|
||||
| GraphQL max depth | 1-50 | Default: 10 |
|
||||
| GraphQL max size | 1KB-1MB | Default: 100KB |
|
||||
| JWT claim nesting | 10 levels max | Use dot notation |
|
||||
| mTLS CA certificates | 5 custom max | CF-managed unlimited |
|
||||
| Schema upload size | 5MB max | Compressed OpenAPI spec |
|
||||
| Volumetric abuse baseline | 7 days training | Initial ML period |
|
||||
| Auth Posture refresh | Daily | Updated nightly |
|
||||
|
||||
## See Also
|
||||
|
||||
- [configuration.md](configuration.md) - Setup guides to avoid common issues
|
||||
- [patterns.md](patterns.md) - Best practices and progressive rollout
|
||||
- [API Shield Docs](https://developers.cloudflare.com/api-shield/)
|
||||
@@ -0,0 +1,185 @@
|
||||
# Patterns & Use Cases
|
||||
|
||||
## Protect API with Schema + JWT
|
||||
|
||||
```bash
|
||||
# 1. Upload OpenAPI schema
|
||||
POST /zones/{zone_id}/api_gateway/user_schemas
|
||||
|
||||
# 2. Configure JWT validation
|
||||
POST /zones/{zone_id}/api_gateway/token_validation
|
||||
{
|
||||
"name": "Auth0",
|
||||
"location": {"header": "Authorization"},
|
||||
"jwks": "{...}"
|
||||
}
|
||||
|
||||
# 3. Create JWT rule
|
||||
POST /zones/{zone_id}/api_gateway/jwt_validation_rules
|
||||
|
||||
# 4. Set schema validation action
|
||||
PUT /zones/{zone_id}/api_gateway/settings/schema_validation
|
||||
{"validation_default_mitigation_action": "block"}
|
||||
```
|
||||
|
||||
## Progressive Rollout
|
||||
|
||||
```
|
||||
1. Log mode: Observe false positives
|
||||
- Schema: Action = Log
|
||||
- JWT: Action = Log
|
||||
|
||||
2. Block subset: Protect critical endpoints
|
||||
- Change specific endpoint actions to Block
|
||||
- Monitor firewall events
|
||||
|
||||
3. Full enforcement: Block all violations
|
||||
- Change default action to Block
|
||||
- Handle fallthrough with custom rule
|
||||
```
|
||||
|
||||
## BOLA Detection
|
||||
|
||||
### Enumeration Detection
|
||||
|
||||
Detects sequential resource access (e.g., `/users/1`, `/users/2`, `/users/3`).
|
||||
|
||||
```javascript
|
||||
// Block BOLA enumeration attempts
|
||||
(cf.api_gateway.cf-risk-bola-enumeration and http.host eq "api.example.com")
|
||||
// Action: Block or Challenge
|
||||
```
|
||||
|
||||
### Parameter Pollution
|
||||
|
||||
Detects duplicate/excessive parameters in requests.
|
||||
|
||||
```javascript
|
||||
// Block parameter pollution
|
||||
(cf.api_gateway.cf-risk-bola-pollution and http.host eq "api.example.com")
|
||||
// Action: Block
|
||||
```
|
||||
|
||||
### Combined BOLA Protection
|
||||
|
||||
```javascript
|
||||
// Comprehensive BOLA rule
|
||||
(cf.api_gateway.cf-risk-bola-enumeration or cf.api_gateway.cf-risk-bola-pollution)
|
||||
and http.host eq "api.example.com"
|
||||
// Action: Block
|
||||
```
|
||||
|
||||
## Authentication Posture
|
||||
|
||||
### Detect Missing Auth
|
||||
|
||||
```javascript
|
||||
// Log endpoints lacking authentication
|
||||
(cf.api_gateway.cf-risk-missing-auth and http.host eq "api.example.com")
|
||||
// Action: Log (for audit)
|
||||
```
|
||||
|
||||
### Detect Mixed Auth
|
||||
|
||||
```javascript
|
||||
// Alert on inconsistent auth patterns
|
||||
(cf.api_gateway.cf-risk-mixed-auth and http.host eq "api.example.com")
|
||||
// Action: Log (review required)
|
||||
```
|
||||
|
||||
## Fallthrough Detection (Shadow APIs)
|
||||
|
||||
```javascript
|
||||
// WAF Custom Rule
|
||||
(cf.api_gateway.fallthrough_triggered and http.host eq "api.example.com")
|
||||
// Action: Log (discover unknown) or Block (strict)
|
||||
```
|
||||
|
||||
## Rate Limiting by User
|
||||
|
||||
```javascript
|
||||
// Rate Limiting Rule (modern syntax)
|
||||
(http.host eq "api.example.com" and
|
||||
is_jwt_valid(http.request.jwt.payload["{config_id}"][0]))
|
||||
|
||||
// Rate: 100 req/60s
|
||||
// Counting expression: lookup_json_string(http.request.jwt.payload["{config_id}"][0], "sub")
|
||||
```
|
||||
|
||||
## Volumetric Abuse Response
|
||||
|
||||
```javascript
|
||||
// Detect abnormal traffic spikes
|
||||
(cf.api_gateway.volumetric_abuse_detected and http.host eq "api.example.com")
|
||||
// Action: Challenge or Rate Limit
|
||||
|
||||
// Combined with rate limiting
|
||||
(cf.api_gateway.volumetric_abuse_detected or
|
||||
cf.threat_score gt 50) and http.host eq "api.example.com"
|
||||
// Action: JS Challenge
|
||||
```
|
||||
|
||||
## GraphQL Protection
|
||||
|
||||
```javascript
|
||||
// Block oversized queries
|
||||
(http.request.uri.path eq "/graphql" and
|
||||
cf.api_gateway.graphql_query_size gt 100000)
|
||||
// Action: Block
|
||||
|
||||
// Block deep nested queries
|
||||
(http.request.uri.path eq "/graphql" and
|
||||
cf.api_gateway.graphql_query_depth gt 10)
|
||||
// Action: Block
|
||||
```
|
||||
|
||||
## Architecture Patterns
|
||||
|
||||
**Public API:** Discovery + Schema Validation 2.0 + JWT + Rate Limiting + Bot Management
|
||||
**Partner API:** mTLS + Schema Validation + Sequence Mitigation
|
||||
**Internal API:** Discovery + Schema Learning + Auth Posture
|
||||
|
||||
## OWASP API Security Top 10 Mapping (2026)
|
||||
|
||||
| OWASP Issue | API Shield Solutions |
|
||||
| ------------------------------------------- | --------------------------------------------------------------------------------------------- |
|
||||
| API1:2023 Broken Object Level Authorization | **BOLA Detection** (enumeration + pollution), Sequence mitigation, Schema, JWT, Rate Limiting |
|
||||
| API2:2023 Broken Authentication | **Auth Posture**, mTLS, JWT validation, Bot Management |
|
||||
| API3:2023 Broken Object Property Auth | Schema validation, JWT validation |
|
||||
| API4:2023 Unrestricted Resource Access | Rate Limiting, **Volumetric Abuse Detection**, **GraphQL Protection**, Bot Management |
|
||||
| API5:2023 Broken Function Level Auth | Schema validation, JWT validation, Auth Posture |
|
||||
| API6:2023 Unrestricted Business Flows | Sequence mitigation, Bot Management |
|
||||
| API7:2023 SSRF | Schema validation, WAF managed rules |
|
||||
| API8:2023 Security Misconfiguration | **Schema Validation 2.0**, Auth Posture, WAF rules |
|
||||
| API9:2023 Improper Inventory Management | **API Discovery**, Schema learning, Auth Posture |
|
||||
| API10:2023 Unsafe API Consumption | JWT validation, Schema validation, WAF managed |
|
||||
|
||||
## Monitoring
|
||||
|
||||
**Security Events:** `Security > Events` → Filter: Action = block, Service = API Shield
|
||||
**Firewall Analytics:** `Analytics > Security` → Filter by `cf.api_gateway.*` fields
|
||||
**Logpush fields:** APIGatewayAuthIDPresent, APIGatewayRequestViolatesSchema, APIGatewayFallthroughDetected, JWTValidationResult
|
||||
|
||||
## Availability (2026)
|
||||
|
||||
| Feature | Availability | Notes |
|
||||
| -------------------------- | ----------------- | -------------------- |
|
||||
| mTLS (CF-managed CA) | All plans | Self-service |
|
||||
| Endpoint Management | All plans | Limited operations |
|
||||
| Schema Validation 2.0 | All plans | Limited operations |
|
||||
| API Discovery | Enterprise | 10K+ ops |
|
||||
| JWT Validation | Enterprise add-on | Full validation |
|
||||
| BOLA Detection | Enterprise add-on | Requires session IDs |
|
||||
| Auth Posture | Enterprise add-on | Security audit |
|
||||
| Volumetric Abuse Detection | Enterprise add-on | Traffic analysis |
|
||||
| GraphQL Protection | Enterprise add-on | Query limits |
|
||||
| Sequence Mitigation | Enterprise (beta) | Contact team |
|
||||
| Full Suite | Enterprise add-on | All features |
|
||||
|
||||
**Enterprise limits:** 10K operations (contact for higher). Preview access available for non-contract evaluation.
|
||||
|
||||
## See Also
|
||||
|
||||
- [configuration.md](configuration.md) - Setup all features before creating rules
|
||||
- [api.md](api.md) - Firewall field reference and API endpoints
|
||||
- [gotchas.md](gotchas.md) - Common issues and limits
|
||||
@@ -0,0 +1,66 @@
|
||||
# Cloudflare API Integration
|
||||
|
||||
Guide for working with Cloudflare's REST API - authentication, SDK usage, common patterns, and troubleshooting.
|
||||
|
||||
## Quick Decision Tree
|
||||
|
||||
```
|
||||
How are you calling the Cloudflare API?
|
||||
├─ From Workers runtime → Use bindings, not REST API (see ../bindings/)
|
||||
├─ Server-side (Node/Python/Go) → Official SDK (see api.md)
|
||||
├─ CLI/scripts → Wrangler or curl (see configuration.md)
|
||||
├─ Infrastructure-as-code → See ../pulumi/ or ../terraform/
|
||||
└─ One-off requests → curl examples (see api.md)
|
||||
```
|
||||
|
||||
## SDK Selection
|
||||
|
||||
| Language | Package | Best For | Default Retries |
|
||||
| ---------- | ------------------ | ------------------------------ | --------------- |
|
||||
| TypeScript | `cloudflare` | Node.js, Bun, Next.js, Workers | 2 |
|
||||
| Python | `cloudflare` | FastAPI, Django, scripts | 2 |
|
||||
| Go | `cloudflare-go/v4` | CLI tools, microservices | 10 |
|
||||
|
||||
All SDKs are Stainless-generated from OpenAPI spec (consistent APIs).
|
||||
|
||||
## Authentication Methods
|
||||
|
||||
| Method | Security | Use Case | Scope |
|
||||
| ---------------- | ------------------- | -------------------- | ------------------- |
|
||||
| **API Token** ✓ | Scoped, rotatable | Production | Per-zone or account |
|
||||
| API Key + Email | Full account access | Legacy only | Everything |
|
||||
| User Service Key | Limited | Origin CA certs only | Origin CA |
|
||||
|
||||
**Always use API tokens** for new projects.
|
||||
|
||||
## Rate Limits
|
||||
|
||||
| Limit | Value |
|
||||
| -------------- | ---------------------------- |
|
||||
| Per user/token | 1200 requests / 5 minutes |
|
||||
| Per IP | 200 requests / second |
|
||||
| GraphQL | 320 / 5 minutes (cost-based) |
|
||||
|
||||
## Reading Order
|
||||
|
||||
| Task | Files to Read |
|
||||
| ---------------------------- | -------------------------------------------------------------------------------- |
|
||||
| Initialize SDK client | api.md |
|
||||
| Configure auth/timeout/retry | configuration.md |
|
||||
| Find usage patterns | patterns.md |
|
||||
| Debug errors/rate limits | gotchas.md |
|
||||
| Product-specific APIs | [Workers docs](https://developers.cloudflare.com/workers/), ../r2/, ../kv/, etc. |
|
||||
|
||||
## In This Reference
|
||||
|
||||
- **[api.md](api.md)** - SDK client initialization, pagination, error handling, examples
|
||||
- **[configuration.md](configuration.md)** - Environment variables, SDK config, Wrangler setup
|
||||
- **[patterns.md](patterns.md)** - Real-world patterns, batch operations, workflows
|
||||
- **[gotchas.md](gotchas.md)** - Rate limits, SDK-specific issues, troubleshooting
|
||||
|
||||
## See Also
|
||||
|
||||
- [Cloudflare API Docs](https://developers.cloudflare.com/api/)
|
||||
- [Bindings Reference](../bindings/) - Workers runtime bindings (preferred over REST API)
|
||||
- [Wrangler Reference](https://developers.cloudflare.com/workers/wrangler/) - CLI tool for Cloudflare development
|
||||
- [GraphQL Analytics API Reference](../graphql-api/) - Analytics data via GraphQL (separate endpoint from REST API)
|
||||
@@ -0,0 +1,205 @@
|
||||
# API Reference
|
||||
|
||||
## Client Initialization
|
||||
|
||||
### TypeScript
|
||||
|
||||
```typescript
|
||||
import Cloudflare from 'cloudflare';
|
||||
|
||||
const client = new Cloudflare({
|
||||
apiToken: process.env.CLOUDFLARE_API_TOKEN
|
||||
});
|
||||
```
|
||||
|
||||
### Python
|
||||
|
||||
```python
|
||||
from cloudflare import Cloudflare
|
||||
|
||||
client = Cloudflare(api_token=os.environ.get("CLOUDFLARE_API_TOKEN"))
|
||||
|
||||
# For async:
|
||||
from cloudflare import AsyncCloudflare
|
||||
client = AsyncCloudflare(api_token=os.environ["CLOUDFLARE_API_TOKEN"])
|
||||
```
|
||||
|
||||
### Go
|
||||
|
||||
```go
|
||||
import (
|
||||
"github.com/cloudflare/cloudflare-go/v4"
|
||||
"github.com/cloudflare/cloudflare-go/v4/option"
|
||||
)
|
||||
|
||||
client := cloudflare.NewClient(
|
||||
option.WithAPIToken(os.Getenv("CLOUDFLARE_API_TOKEN")),
|
||||
)
|
||||
```
|
||||
|
||||
## Authentication
|
||||
|
||||
### API Token (Recommended)
|
||||
|
||||
**Create token**: Dashboard → My Profile → API Tokens → Create Token
|
||||
|
||||
```bash
|
||||
export CLOUDFLARE_API_TOKEN='your-token-here'
|
||||
|
||||
curl "https://api.cloudflare.com/client/v4/zones" \
|
||||
--header "Authorization: Bearer $CLOUDFLARE_API_TOKEN"
|
||||
```
|
||||
|
||||
**Token scopes**: Always use minimal permissions (zone-specific, time-limited).
|
||||
|
||||
### API Key (Legacy)
|
||||
|
||||
```bash
|
||||
curl "https://api.cloudflare.com/client/v4/zones" \
|
||||
--header "X-Auth-Email: user@example.com" \
|
||||
--header "X-Auth-Key: $CLOUDFLARE_API_KEY"
|
||||
```
|
||||
|
||||
**Not recommended:** Full account access, cannot scope permissions.
|
||||
|
||||
## Auto-Pagination
|
||||
|
||||
All SDKs support automatic pagination for list operations.
|
||||
|
||||
```typescript
|
||||
// TypeScript: for await...of
|
||||
for await (const zone of client.zones.list()) {
|
||||
console.log(zone.id);
|
||||
}
|
||||
```
|
||||
|
||||
```python
|
||||
# Python: iterator protocol
|
||||
for zone in client.zones.list():
|
||||
print(zone.id)
|
||||
```
|
||||
|
||||
```go
|
||||
// Go: ListAutoPaging
|
||||
iter := client.Zones.ListAutoPaging(ctx, cloudflare.ZoneListParams{})
|
||||
for iter.Next() {
|
||||
zone := iter.Current()
|
||||
fmt.Println(zone.ID)
|
||||
}
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
||||
```typescript
|
||||
try {
|
||||
const zone = await client.zones.get({ zone_id: 'xxx' });
|
||||
} catch (err) {
|
||||
if (err instanceof Cloudflare.NotFoundError) {
|
||||
// 404
|
||||
} else if (err instanceof Cloudflare.RateLimitError) {
|
||||
// 429 - SDK auto-retries with backoff
|
||||
} else if (err instanceof Cloudflare.APIError) {
|
||||
console.log(err.status, err.message);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Common Error Types:**
|
||||
|
||||
- `AuthenticationError` (401) - Invalid token
|
||||
- `PermissionDeniedError` (403) - Insufficient scope
|
||||
- `NotFoundError` (404) - Resource not found
|
||||
- `RateLimitError` (429) - Rate limit exceeded
|
||||
- `InternalServerError` (≥500) - Cloudflare error
|
||||
|
||||
## Zone Management
|
||||
|
||||
```typescript
|
||||
// List zones
|
||||
const zones = await client.zones.list({
|
||||
account: { id: 'account-id' },
|
||||
status: 'active'
|
||||
});
|
||||
|
||||
// Create zone
|
||||
const zone = await client.zones.create({
|
||||
account: { id: 'account-id' },
|
||||
name: 'example.com',
|
||||
type: 'full' // or 'partial'
|
||||
});
|
||||
|
||||
// Update zone
|
||||
await client.zones.edit('zone-id', {
|
||||
paused: false
|
||||
});
|
||||
|
||||
// Delete zone
|
||||
await client.zones.delete('zone-id');
|
||||
```
|
||||
|
||||
```go
|
||||
// Go: requires cloudflare.F() wrapper
|
||||
zone, err := client.Zones.New(ctx, cloudflare.ZoneNewParams{
|
||||
Account: cloudflare.F(cloudflare.ZoneNewParamsAccount{
|
||||
ID: cloudflare.F("account-id"),
|
||||
}),
|
||||
Name: cloudflare.F("example.com"),
|
||||
Type: cloudflare.F(cloudflare.ZoneNewParamsTypeFull),
|
||||
})
|
||||
```
|
||||
|
||||
## DNS Management
|
||||
|
||||
```typescript
|
||||
// Create DNS record
|
||||
await client.dns.records.create({
|
||||
zone_id: 'zone-id',
|
||||
type: 'A',
|
||||
name: 'subdomain.example.com',
|
||||
content: '192.0.2.1',
|
||||
ttl: 1, // auto
|
||||
proxied: true // Orange cloud
|
||||
});
|
||||
|
||||
// List DNS records (with auto-pagination)
|
||||
for await (const record of client.dns.records.list({
|
||||
zone_id: 'zone-id',
|
||||
type: 'A'
|
||||
})) {
|
||||
console.log(record.name, record.content);
|
||||
}
|
||||
|
||||
// Update DNS record
|
||||
await client.dns.records.update({
|
||||
zone_id: 'zone-id',
|
||||
dns_record_id: 'record-id',
|
||||
type: 'A',
|
||||
name: 'subdomain.example.com',
|
||||
content: '203.0.113.1',
|
||||
proxied: true
|
||||
});
|
||||
|
||||
// Delete DNS record
|
||||
await client.dns.records.delete({
|
||||
zone_id: 'zone-id',
|
||||
dns_record_id: 'record-id'
|
||||
});
|
||||
```
|
||||
|
||||
```python
|
||||
# Python example
|
||||
client.dns.records.create(
|
||||
zone_id="zone-id",
|
||||
type="A",
|
||||
name="subdomain.example.com",
|
||||
content="192.0.2.1",
|
||||
ttl=1,
|
||||
proxied=True,
|
||||
)
|
||||
```
|
||||
|
||||
## See Also
|
||||
|
||||
- [configuration.md](./configuration.md) - SDK configuration, environment variables
|
||||
- [patterns.md](./patterns.md) - Real-world patterns and workflows
|
||||
- [gotchas.md](./gotchas.md) - Rate limits, troubleshooting
|
||||
@@ -0,0 +1,158 @@
|
||||
# Configuration
|
||||
|
||||
## Environment Variables
|
||||
|
||||
### Set Variables
|
||||
|
||||
| Platform | Command |
|
||||
| ----------- | ------------------------------------- |
|
||||
| Linux/macOS | `export CLOUDFLARE_API_TOKEN='token'` |
|
||||
| PowerShell | `$env:CLOUDFLARE_API_TOKEN = 'token'` |
|
||||
| Windows CMD | `set CLOUDFLARE_API_TOKEN=token` |
|
||||
|
||||
**Security:** Never commit tokens. Use `.env` files (gitignored) or secret managers.
|
||||
|
||||
### .env File Pattern
|
||||
|
||||
```bash
|
||||
# .env (add to .gitignore)
|
||||
CLOUDFLARE_API_TOKEN=your-token-here
|
||||
CLOUDFLARE_ACCOUNT_ID=your-account-id
|
||||
```
|
||||
|
||||
```typescript
|
||||
// TypeScript
|
||||
import 'dotenv/config';
|
||||
|
||||
const client = new Cloudflare({
|
||||
apiToken: process.env.CLOUDFLARE_API_TOKEN
|
||||
});
|
||||
```
|
||||
|
||||
```python
|
||||
# Python
|
||||
from dotenv import load_dotenv
|
||||
load_dotenv()
|
||||
|
||||
client = Cloudflare(api_token=os.environ["CLOUDFLARE_API_TOKEN"])
|
||||
```
|
||||
|
||||
## SDK Configuration
|
||||
|
||||
### TypeScript
|
||||
|
||||
```typescript
|
||||
const client = new Cloudflare({
|
||||
apiToken: process.env.CLOUDFLARE_API_TOKEN,
|
||||
timeout: 120000, // 2 min (default 60s), in milliseconds
|
||||
maxRetries: 5, // default 2
|
||||
baseURL: 'https://...' // proxy (rare)
|
||||
});
|
||||
|
||||
// Per-request overrides
|
||||
await client.zones.get({ zone_id: 'zone-id' }, { timeout: 5000, maxRetries: 0 });
|
||||
```
|
||||
|
||||
### Python
|
||||
|
||||
```python
|
||||
client = Cloudflare(
|
||||
api_token=os.environ["CLOUDFLARE_API_TOKEN"],
|
||||
timeout=120, # seconds (default 60)
|
||||
max_retries=5, # default 2
|
||||
base_url="https://...", # proxy (rare)
|
||||
)
|
||||
|
||||
# Per-request overrides
|
||||
client.with_options(timeout=5, max_retries=0).zones.get(zone_id="zone-id")
|
||||
```
|
||||
|
||||
### Go
|
||||
|
||||
```go
|
||||
client := cloudflare.NewClient(
|
||||
option.WithAPIToken(os.Getenv("CLOUDFLARE_API_TOKEN")),
|
||||
option.WithMaxRetries(5), // default 10 (higher than TS/Python)
|
||||
option.WithRequestTimeout(2 * time.Minute), // default 60s
|
||||
option.WithBaseURL("https://..."), // proxy (rare)
|
||||
)
|
||||
|
||||
// Per-request overrides
|
||||
client.Zones.Get(ctx, "zone-id", option.WithMaxRetries(0))
|
||||
```
|
||||
|
||||
## Configuration Options
|
||||
|
||||
| Option | TypeScript | Python | Go | Default |
|
||||
| -------- | -------------- | ------------- | -------------------- | ------------------ |
|
||||
| Timeout | `timeout` (ms) | `timeout` (s) | `WithRequestTimeout` | 60s |
|
||||
| Retries | `maxRetries` | `max_retries` | `WithMaxRetries` | 2 (Go: 10) |
|
||||
| Base URL | `baseURL` | `base_url` | `WithBaseURL` | api.cloudflare.com |
|
||||
|
||||
**Note:** Go SDK has higher default retries (10) than TypeScript/Python (2).
|
||||
|
||||
## Timeout Configuration
|
||||
|
||||
**When to increase:**
|
||||
|
||||
- Large zone transfers
|
||||
- Bulk DNS operations
|
||||
- Worker script uploads
|
||||
|
||||
```typescript
|
||||
const client = new Cloudflare({
|
||||
timeout: 300000 // 5 minutes
|
||||
});
|
||||
```
|
||||
|
||||
## Retry Configuration
|
||||
|
||||
**When to increase:** Rate-limit-heavy workflows, flaky network
|
||||
|
||||
**When to decrease:** Fast-fail requirements, user-facing requests
|
||||
|
||||
```typescript
|
||||
// Increase retries for batch operations
|
||||
const client = new Cloudflare({ maxRetries: 10 });
|
||||
|
||||
// Disable retries for fast-fail
|
||||
const fastClient = new Cloudflare({ maxRetries: 0 });
|
||||
```
|
||||
|
||||
## Wrangler CLI Integration
|
||||
|
||||
```bash
|
||||
# Configure authentication
|
||||
wrangler login
|
||||
# Or
|
||||
export CLOUDFLARE_API_TOKEN='token'
|
||||
|
||||
# Common commands that use API
|
||||
wrangler deploy # Uploads worker via API
|
||||
wrangler kv:key put # KV operations
|
||||
wrangler r2 bucket create # R2 operations
|
||||
wrangler d1 execute # D1 operations
|
||||
wrangler pages deploy # Pages operations
|
||||
|
||||
# Get API configuration
|
||||
wrangler whoami # Shows authenticated user
|
||||
```
|
||||
|
||||
### wrangler.toml
|
||||
|
||||
```toml
|
||||
name = "my-worker"
|
||||
main = "src/index.ts"
|
||||
compatibility_date = "2024-01-01"
|
||||
account_id = "your-account-id"
|
||||
|
||||
# Can also use env vars:
|
||||
# CLOUDFLARE_ACCOUNT_ID
|
||||
# CLOUDFLARE_API_TOKEN
|
||||
```
|
||||
|
||||
## See Also
|
||||
|
||||
- [api.md](./api.md) - Client initialization, authentication
|
||||
- [gotchas.md](./gotchas.md) - Rate limits, timeout errors
|
||||
- [Wrangler Reference](https://developers.cloudflare.com/workers/wrangler/) - CLI tool details
|
||||
@@ -0,0 +1,231 @@
|
||||
# Gotchas & Troubleshooting
|
||||
|
||||
## Rate Limits & 429 Errors
|
||||
|
||||
**Actual Limits:**
|
||||
|
||||
- **1200 requests / 5 minutes** per user/token (global)
|
||||
- **200 requests / second** per IP address
|
||||
- **GraphQL: 320 / 5 minutes** (cost-based)
|
||||
|
||||
**SDK Behavior:**
|
||||
|
||||
- Auto-retry with exponential backoff (default 2 retries, Go: 10)
|
||||
- Respects `Retry-After` header
|
||||
- Throws `RateLimitError` after exhausting retries
|
||||
|
||||
**Solution:**
|
||||
|
||||
```typescript
|
||||
// Increase retries for rate-limit-heavy workflows
|
||||
const client = new Cloudflare({ maxRetries: 5 });
|
||||
|
||||
// Add application-level throttling
|
||||
import pLimit from 'p-limit';
|
||||
const limit = pLimit(10); // Max 10 concurrent requests
|
||||
```
|
||||
|
||||
## SDK-Specific Issues
|
||||
|
||||
### Go: Required Field Wrapper
|
||||
|
||||
**Problem:** Go SDK requires `cloudflare.F()` wrapper for optional fields.
|
||||
|
||||
```go
|
||||
// ❌ WRONG - Won't compile or send field
|
||||
client.Zones.New(ctx, cloudflare.ZoneNewParams{
|
||||
Name: "example.com",
|
||||
})
|
||||
|
||||
// ✅ CORRECT
|
||||
client.Zones.New(ctx, cloudflare.ZoneNewParams{
|
||||
Name: cloudflare.F("example.com"),
|
||||
Account: cloudflare.F(cloudflare.ZoneNewParamsAccount{
|
||||
ID: cloudflare.F("account-id"),
|
||||
}),
|
||||
})
|
||||
```
|
||||
|
||||
**Why:** Distinguishes between zero value, null, and omitted fields.
|
||||
|
||||
### Python: Async vs Sync Clients
|
||||
|
||||
**Problem:** Using sync client in async context or vice versa.
|
||||
|
||||
```python
|
||||
# ❌ WRONG - Can't await sync client
|
||||
from cloudflare import Cloudflare
|
||||
client = Cloudflare()
|
||||
await client.zones.list() # TypeError
|
||||
|
||||
# ✅ CORRECT - Use AsyncCloudflare
|
||||
from cloudflare import AsyncCloudflare
|
||||
client = AsyncCloudflare()
|
||||
await client.zones.list()
|
||||
```
|
||||
|
||||
## Token Permission Errors (403)
|
||||
|
||||
**Problem:** API returns 403 Forbidden despite valid token.
|
||||
|
||||
**Cause:** Token lacks required permissions (scope).
|
||||
|
||||
**Scopes Required:**
|
||||
|
||||
| Operation | Required Scope |
|
||||
| ------------- | --------------------------------------- |
|
||||
| List zones | Zone:Read (zone-level or account-level) |
|
||||
| Create zone | Zone:Edit (account-level) |
|
||||
| Edit DNS | DNS:Edit (zone-level) |
|
||||
| Deploy Worker | Workers Script:Edit (account-level) |
|
||||
| Read KV | Workers KV Storage:Read |
|
||||
| Write KV | Workers KV Storage:Edit |
|
||||
|
||||
**Solution:** Re-create token with correct permissions in Dashboard → My Profile → API Tokens.
|
||||
|
||||
## Pagination Truncation
|
||||
|
||||
**Problem:** Only getting first 20 results (default page size).
|
||||
|
||||
**Solution:** Use auto-pagination iterators.
|
||||
|
||||
```typescript
|
||||
// ❌ WRONG - Only first page (20 items)
|
||||
const page = await client.zones.list();
|
||||
|
||||
// ✅ CORRECT - All results
|
||||
const zones = [];
|
||||
for await (const zone of client.zones.list()) {
|
||||
zones.push(zone);
|
||||
}
|
||||
```
|
||||
|
||||
## Workers Subrequests
|
||||
|
||||
**Problem:** Rate limit hit faster than expected in Workers.
|
||||
|
||||
**Cause:** Workers subrequests count as separate API calls.
|
||||
|
||||
**Solution:** Use bindings instead of REST API in Workers (see ../bindings/).
|
||||
|
||||
```typescript
|
||||
// ❌ WRONG - REST API in Workers (counts against rate limit)
|
||||
const client = new Cloudflare({ apiToken: env.CLOUDFLARE_API_TOKEN });
|
||||
const zones = await client.zones.list();
|
||||
|
||||
// ✅ CORRECT - Use bindings (no rate limit)
|
||||
// Access via env.MY_BINDING
|
||||
```
|
||||
|
||||
## Authentication Errors (401)
|
||||
|
||||
**Problem:** "Authentication failed" or "Invalid token"
|
||||
|
||||
**Causes:**
|
||||
|
||||
- Token expired
|
||||
- Token deleted/revoked
|
||||
- Token not set in environment
|
||||
- Wrong token format
|
||||
|
||||
**Solution:**
|
||||
|
||||
```typescript
|
||||
// Verify token is set
|
||||
if (!process.env.CLOUDFLARE_API_TOKEN) {
|
||||
throw new Error('CLOUDFLARE_API_TOKEN not set');
|
||||
}
|
||||
|
||||
// Test token
|
||||
const user = await client.user.tokens.verify();
|
||||
console.log('Token valid:', user.status);
|
||||
```
|
||||
|
||||
## Timeout Errors
|
||||
|
||||
**Problem:** Request times out (default 60s).
|
||||
|
||||
**Cause:** Large operations (bulk DNS, zone transfers).
|
||||
|
||||
**Solution:** Increase timeout or split operations.
|
||||
|
||||
```typescript
|
||||
// Increase timeout
|
||||
const client = new Cloudflare({
|
||||
timeout: 300000 // 5 minutes
|
||||
});
|
||||
|
||||
// Or split operations
|
||||
const batchSize = 100;
|
||||
for (let i = 0; i < records.length; i += batchSize) {
|
||||
const batch = records.slice(i, i + batchSize);
|
||||
await processBatch(batch);
|
||||
}
|
||||
```
|
||||
|
||||
## Zone Not Found (404)
|
||||
|
||||
**Problem:** Zone ID valid but returns 404.
|
||||
|
||||
**Causes:**
|
||||
|
||||
- Zone not in account associated with token
|
||||
- Zone deleted
|
||||
- Wrong zone ID format
|
||||
|
||||
**Solution:**
|
||||
|
||||
```typescript
|
||||
// List all zones to find correct ID
|
||||
for await (const zone of client.zones.list()) {
|
||||
console.log(zone.id, zone.name);
|
||||
}
|
||||
```
|
||||
|
||||
## Limits Reference
|
||||
|
||||
| Resource/Limit | Value | Notes |
|
||||
| ------------------------------- | --------- | ---------------------- |
|
||||
| API rate limit | 1200/5min | Per user/token |
|
||||
| IP rate limit | 200/sec | Per IP |
|
||||
| GraphQL rate limit | 320/5min | Cost-based |
|
||||
| Parallel requests (recommended) | < 10 | Avoid overwhelming API |
|
||||
| Default page size | 20 | Use auto-pagination |
|
||||
| Max page size | 50 | Some endpoints |
|
||||
|
||||
## Best Practices
|
||||
|
||||
**Security:**
|
||||
|
||||
- Never commit tokens
|
||||
- Use minimal permissions
|
||||
- Rotate tokens regularly
|
||||
- Set token expiration
|
||||
|
||||
**Performance:**
|
||||
|
||||
- Batch operations
|
||||
- Use pagination wisely
|
||||
- Cache responses
|
||||
- Handle rate limits
|
||||
|
||||
**Code Organization:**
|
||||
|
||||
```typescript
|
||||
// Create reusable client instance
|
||||
export const cfClient = new Cloudflare({
|
||||
apiToken: process.env.CLOUDFLARE_API_TOKEN,
|
||||
maxRetries: 5
|
||||
});
|
||||
|
||||
// Wrap common operations
|
||||
export async function getZoneDetails(zoneId: string) {
|
||||
return await cfClient.zones.get({ zone_id: zoneId });
|
||||
}
|
||||
```
|
||||
|
||||
## See Also
|
||||
|
||||
- [api.md](./api.md) - Error types, authentication
|
||||
- [configuration.md](./configuration.md) - Timeout/retry configuration
|
||||
- [patterns.md](./patterns.md) - Error handling patterns
|
||||
@@ -0,0 +1,206 @@
|
||||
# Common Patterns
|
||||
|
||||
## List All with Auto-Pagination
|
||||
|
||||
**Problem:** API returns paginated results. Default page size is 20.
|
||||
|
||||
**Solution:** Use SDK auto-pagination to iterate all results.
|
||||
|
||||
```typescript
|
||||
// TypeScript
|
||||
for await (const zone of client.zones.list()) {
|
||||
console.log(zone.name);
|
||||
}
|
||||
```
|
||||
|
||||
```python
|
||||
# Python
|
||||
for zone in client.zones.list():
|
||||
print(zone.name)
|
||||
```
|
||||
|
||||
```go
|
||||
// Go
|
||||
iter := client.Zones.ListAutoPaging(ctx, cloudflare.ZoneListParams{})
|
||||
for iter.Next() {
|
||||
fmt.Println(iter.Current().Name)
|
||||
}
|
||||
```
|
||||
|
||||
## Error Handling with Retry
|
||||
|
||||
**Problem:** Rate limits (429) and transient errors need retry.
|
||||
|
||||
**Solution:** SDKs auto-retry with exponential backoff. Customize as needed.
|
||||
|
||||
```typescript
|
||||
// Increase retries for rate-limit-heavy operations
|
||||
const client = new Cloudflare({ maxRetries: 5 });
|
||||
|
||||
try {
|
||||
const zone = await client.zones.create({/* ... */});
|
||||
} catch (err) {
|
||||
if (err instanceof Cloudflare.RateLimitError) {
|
||||
// Already retried 5 times with backoff
|
||||
const retryAfter = err.headers['retry-after'];
|
||||
console.log(`Rate limited. Retry after ${retryAfter}s`);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Batch Parallel Operations
|
||||
|
||||
**Problem:** Need to create multiple resources quickly.
|
||||
|
||||
**Solution:** Use `Promise.all()` for parallel requests (respect rate limits).
|
||||
|
||||
```typescript
|
||||
// Create multiple DNS records in parallel
|
||||
const records = ['www', 'api', 'cdn'].map((subdomain) =>
|
||||
client.dns.records.create({
|
||||
zone_id: 'zone-id',
|
||||
type: 'A',
|
||||
name: `${subdomain}.example.com`,
|
||||
content: '192.0.2.1'
|
||||
})
|
||||
);
|
||||
await Promise.all(records);
|
||||
```
|
||||
|
||||
**Controlled concurrency** (avoid rate limits):
|
||||
|
||||
```typescript
|
||||
import pLimit from 'p-limit';
|
||||
const limit = pLimit(10); // Max 10 concurrent
|
||||
|
||||
const subdomains = ['www', 'api', 'cdn' /* many more */];
|
||||
const records = subdomains.map((subdomain) =>
|
||||
limit(() =>
|
||||
client.dns.records.create({
|
||||
zone_id: 'zone-id',
|
||||
type: 'A',
|
||||
name: `${subdomain}.example.com`,
|
||||
content: '192.0.2.1'
|
||||
})
|
||||
)
|
||||
);
|
||||
await Promise.all(records);
|
||||
```
|
||||
|
||||
## Zone CRUD Workflow
|
||||
|
||||
```typescript
|
||||
// Create
|
||||
const zone = await client.zones.create({
|
||||
account: { id: 'account-id' },
|
||||
name: 'example.com',
|
||||
type: 'full'
|
||||
});
|
||||
|
||||
// Read
|
||||
const fetched = await client.zones.get({ zone_id: zone.id });
|
||||
|
||||
// Update
|
||||
await client.zones.edit(zone.id, { paused: false });
|
||||
|
||||
// Delete
|
||||
await client.zones.delete(zone.id);
|
||||
```
|
||||
|
||||
## DNS Bulk Update
|
||||
|
||||
```typescript
|
||||
// Fetch all A records
|
||||
const records = [];
|
||||
for await (const record of client.dns.records.list({
|
||||
zone_id: 'zone-id',
|
||||
type: 'A'
|
||||
})) {
|
||||
records.push(record);
|
||||
}
|
||||
|
||||
// Update all to new IP
|
||||
await Promise.all(
|
||||
records.map((record) =>
|
||||
client.dns.records.update({
|
||||
zone_id: 'zone-id',
|
||||
dns_record_id: record.id,
|
||||
type: 'A',
|
||||
name: record.name,
|
||||
content: '203.0.113.1', // New IP
|
||||
proxied: record.proxied,
|
||||
ttl: record.ttl
|
||||
})
|
||||
)
|
||||
);
|
||||
```
|
||||
|
||||
## Filter and Collect Results
|
||||
|
||||
```typescript
|
||||
// Find all proxied A records
|
||||
const proxiedRecords = [];
|
||||
for await (const record of client.dns.records.list({
|
||||
zone_id: 'zone-id',
|
||||
type: 'A'
|
||||
})) {
|
||||
if (record.proxied) {
|
||||
proxiedRecords.push(record);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Error Recovery Pattern
|
||||
|
||||
```typescript
|
||||
async function createZoneWithRetry(name: string, maxAttempts = 3) {
|
||||
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
||||
try {
|
||||
return await client.zones.create({
|
||||
account: { id: 'account-id' },
|
||||
name,
|
||||
type: 'full'
|
||||
});
|
||||
} catch (err) {
|
||||
if (err instanceof Cloudflare.RateLimitError && attempt < maxAttempts) {
|
||||
const retryAfter = parseInt(err.headers['retry-after'] || '5');
|
||||
console.log(`Rate limited, waiting ${retryAfter}s (retry ${attempt}/${maxAttempts})`);
|
||||
await new Promise((resolve) => setTimeout(resolve, retryAfter * 1000));
|
||||
} else {
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Conditional Update Pattern
|
||||
|
||||
```typescript
|
||||
// Only update if zone is active
|
||||
const zone = await client.zones.get({ zone_id: 'zone-id' });
|
||||
if (zone.status === 'active') {
|
||||
await client.zones.edit(zone.id, { paused: false });
|
||||
}
|
||||
```
|
||||
|
||||
## Batch with Error Handling
|
||||
|
||||
```typescript
|
||||
// Process multiple zones, continue on errors
|
||||
const results = await Promise.allSettled(zoneIds.map((id) => client.zones.get({ zone_id: id })));
|
||||
|
||||
results.forEach((result, i) => {
|
||||
if (result.status === 'fulfilled') {
|
||||
console.log(`Zone ${i}: ${result.value.name}`);
|
||||
} else {
|
||||
console.error(`Zone ${i} failed:`, result.reason.message);
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
## See Also
|
||||
|
||||
- [api.md](./api.md) - SDK client initialization, basic operations
|
||||
- [gotchas.md](./gotchas.md) - Rate limits, common errors
|
||||
- [configuration.md](./configuration.md) - SDK configuration options
|
||||
@@ -0,0 +1,96 @@
|
||||
# Cloudflare Argo Smart Routing Skill Reference
|
||||
|
||||
## Overview
|
||||
|
||||
Cloudflare Argo Smart Routing is a performance optimization service that detects real-time network issues and routes web traffic across the most efficient network path. It continuously monitors network conditions and intelligently routes traffic through the fastest, most reliable routes in Cloudflare's network.
|
||||
|
||||
**Note on Smart Shield:** Argo Smart Routing is being integrated into Cloudflare's Smart Shield product for enhanced DDoS protection and performance. Existing Argo customers maintain full functionality with gradual migration to Smart Shield features.
|
||||
|
||||
## Quick Start
|
||||
|
||||
### Enable via cURL
|
||||
|
||||
```bash
|
||||
curl -X PATCH "https://api.cloudflare.com/client/v4/zones/{zone_id}/argo/smart_routing" \
|
||||
-H "Authorization: Bearer YOUR_API_TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"value": "on"}'
|
||||
```
|
||||
|
||||
### Enable via TypeScript SDK
|
||||
|
||||
```typescript
|
||||
import Cloudflare from 'cloudflare';
|
||||
|
||||
const client = new Cloudflare({ apiToken: process.env.CLOUDFLARE_API_TOKEN });
|
||||
|
||||
const result = await client.argo.smartRouting.edit({
|
||||
zone_id: 'your-zone-id',
|
||||
value: 'on'
|
||||
});
|
||||
|
||||
console.log(`Argo enabled: ${result.value}`);
|
||||
```
|
||||
|
||||
## Core Concepts
|
||||
|
||||
### What It Does
|
||||
|
||||
- **Intelligent routing**: Detects congestion, outages, packet loss in real-time
|
||||
- **Global optimization**: Routes across 300+ Cloudflare data centers
|
||||
- **Automatic failover**: Switches paths when issues detected (typically <1s)
|
||||
- **Works with existing setup**: No origin changes required
|
||||
|
||||
### Billing Model
|
||||
|
||||
- Usage-based: Charged per GB of traffic (excluding DDoS/WAF mitigated traffic)
|
||||
- Requires billing configuration before enabling
|
||||
- Available on Enterprise+ plans (check zone eligibility)
|
||||
|
||||
### When to Use
|
||||
|
||||
- **High-traffic production sites** with global user base
|
||||
- **Latency-sensitive applications** (APIs, real-time services)
|
||||
- **Sites behind Cloudflare proxy** (orange-clouded DNS records)
|
||||
- **Combined with Tiered Cache** for maximum performance gains
|
||||
|
||||
### When NOT to Use
|
||||
|
||||
- Development/staging environments (cost control)
|
||||
- Low-traffic sites (<1TB/month) where cost may exceed benefit
|
||||
- Sites with primarily single-region traffic
|
||||
|
||||
## Should I Enable Argo?
|
||||
|
||||
| Your Situation | Recommendation |
|
||||
| ----------------------------------------- | ----------------------------------- |
|
||||
| Global production app, >1TB/month traffic | ✅ Enable - likely ROI positive |
|
||||
| Enterprise plan, latency-critical APIs | ✅ Enable - performance matters |
|
||||
| Regional site, <100GB/month traffic | ⚠️ Evaluate - cost may not justify |
|
||||
| Development/staging environment | ❌ Disable - use in production only |
|
||||
| Not yet configured billing | ❌ Configure billing first |
|
||||
|
||||
## Reading Order by Task
|
||||
|
||||
| Your Goal | Start With | Then Read |
|
||||
| ----------------------------- | -------------------------------------------------------- | -------------------------- |
|
||||
| Enable Argo for first time | Quick Start above → [configuration.md](configuration.md) | [gotchas.md](gotchas.md) |
|
||||
| Use TypeScript/Python SDK | [api.md](api.md) | [patterns.md](patterns.md) |
|
||||
| Terraform/IaC setup | [configuration.md](configuration.md) | - |
|
||||
| Enable for Spectrum TCP app | [patterns.md](patterns.md) → Spectrum section | [api.md](api.md) |
|
||||
| Troubleshoot enablement issue | [gotchas.md](gotchas.md) | [api.md](api.md) |
|
||||
| Manage billing/usage | [patterns.md](patterns.md) → Billing section | [gotchas.md](gotchas.md) |
|
||||
|
||||
## In This Reference
|
||||
|
||||
- **[api.md](api.md)** - API endpoints, SDK methods, error handling, Python/TypeScript examples
|
||||
- **[configuration.md](configuration.md)** - Terraform setup, environment config, billing configuration
|
||||
- **[patterns.md](patterns.md)** - Tiered Cache integration, Spectrum TCP apps, billing management, validation patterns
|
||||
- **[gotchas.md](gotchas.md)** - Common errors, permission issues, limits, best practices
|
||||
|
||||
## See Also
|
||||
|
||||
- [Cloudflare Argo Smart Routing Docs](https://developers.cloudflare.com/argo-smart-routing/)
|
||||
- [Cloudflare Smart Shield](https://developers.cloudflare.com/smart-shield/)
|
||||
- [Spectrum Documentation](https://developers.cloudflare.com/spectrum/)
|
||||
- [Tiered Cache](https://developers.cloudflare.com/cache/how-to/tiered-cache/)
|
||||
@@ -0,0 +1,253 @@
|
||||
## API Reference
|
||||
|
||||
**Note on Smart Shield:** Argo Smart Routing is being integrated into Cloudflare's Smart Shield product. API endpoints remain stable; existing integrations continue to work without changes.
|
||||
|
||||
### Base Endpoint
|
||||
|
||||
```
|
||||
https://api.cloudflare.com/client/v4
|
||||
```
|
||||
|
||||
### Authentication
|
||||
|
||||
Use API tokens with Zone:Argo Smart Routing:Edit permissions:
|
||||
|
||||
```bash
|
||||
# Headers required
|
||||
X-Auth-Email: user@example.com
|
||||
Authorization: Bearer YOUR_API_TOKEN
|
||||
```
|
||||
|
||||
### Get Argo Smart Routing Status
|
||||
|
||||
**Endpoint:** `GET /zones/{zone_id}/argo/smart_routing`
|
||||
|
||||
**Description:** Retrieves current Argo Smart Routing enablement status.
|
||||
|
||||
**cURL Example:**
|
||||
|
||||
```bash
|
||||
curl -X GET "https://api.cloudflare.com/client/v4/zones/{zone_id}/argo/smart_routing" \
|
||||
-H "Authorization: Bearer YOUR_API_TOKEN" \
|
||||
-H "Content-Type: application/json"
|
||||
```
|
||||
|
||||
**Response:**
|
||||
|
||||
```json
|
||||
{
|
||||
"result": {
|
||||
"id": "smart_routing",
|
||||
"value": "on",
|
||||
"editable": true,
|
||||
"modified_on": "2024-01-11T12:00:00Z"
|
||||
},
|
||||
"success": true,
|
||||
"errors": [],
|
||||
"messages": []
|
||||
}
|
||||
```
|
||||
|
||||
**TypeScript SDK Example:**
|
||||
|
||||
```typescript
|
||||
import Cloudflare from 'cloudflare';
|
||||
|
||||
const client = new Cloudflare({
|
||||
apiToken: process.env.CLOUDFLARE_API_TOKEN
|
||||
});
|
||||
|
||||
const status = await client.argo.smartRouting.get({ zone_id: 'your-zone-id' });
|
||||
console.log(`Argo status: ${status.value}, editable: ${status.editable}`);
|
||||
```
|
||||
|
||||
**Python SDK Example:**
|
||||
|
||||
```python
|
||||
from cloudflare import Cloudflare
|
||||
|
||||
client = Cloudflare(api_token=os.environ.get('CLOUDFLARE_API_TOKEN'))
|
||||
|
||||
status = client.argo.smart_routing.get(zone_id='your-zone-id')
|
||||
print(f"Argo status: {status.value}, editable: {status.editable}")
|
||||
```
|
||||
|
||||
### Update Argo Smart Routing Status
|
||||
|
||||
**Endpoint:** `PATCH /zones/{zone_id}/argo/smart_routing`
|
||||
|
||||
**Description:** Enable or disable Argo Smart Routing for a zone.
|
||||
|
||||
**Request Body:**
|
||||
|
||||
```json
|
||||
{
|
||||
"value": "on" // or "off"
|
||||
}
|
||||
```
|
||||
|
||||
**cURL Example:**
|
||||
|
||||
```bash
|
||||
curl -X PATCH "https://api.cloudflare.com/client/v4/zones/{zone_id}/argo/smart_routing" \
|
||||
-H "Authorization: Bearer YOUR_API_TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"value": "on"}'
|
||||
```
|
||||
|
||||
**TypeScript SDK Example:**
|
||||
|
||||
```typescript
|
||||
const result = await client.argo.smartRouting.edit({
|
||||
zone_id: 'your-zone-id',
|
||||
value: 'on'
|
||||
});
|
||||
console.log(`Updated: ${result.value} at ${result.modified_on}`);
|
||||
```
|
||||
|
||||
**Python SDK Example:**
|
||||
|
||||
```python
|
||||
result = client.argo.smart_routing.edit(
|
||||
zone_id='your-zone-id',
|
||||
value='on'
|
||||
)
|
||||
print(f"Updated: {result.value} at {result.modified_on}")
|
||||
```
|
||||
|
||||
## Checking Editability Before Updates
|
||||
|
||||
**Critical:** Always check the `editable` field before attempting to enable/disable Argo. When `editable: false`, the zone has restrictions (billing not configured, insufficient permissions, or plan limitations).
|
||||
|
||||
**Pattern:**
|
||||
|
||||
```typescript
|
||||
async function safelyEnableArgo(client: Cloudflare, zoneId: string): Promise<boolean> {
|
||||
const status = await client.argo.smartRouting.get({ zone_id: zoneId });
|
||||
|
||||
if (!status.editable) {
|
||||
console.error('Cannot modify Argo: editable=false (check billing/permissions)');
|
||||
return false;
|
||||
}
|
||||
|
||||
if (status.value === 'on') {
|
||||
console.log('Argo already enabled');
|
||||
return true;
|
||||
}
|
||||
|
||||
await client.argo.smartRouting.edit({ zone_id: zoneId, value: 'on' });
|
||||
console.log('Argo enabled successfully');
|
||||
return true;
|
||||
}
|
||||
```
|
||||
|
||||
**Python Pattern:**
|
||||
|
||||
```python
|
||||
def safely_enable_argo(client: Cloudflare, zone_id: str) -> bool:
|
||||
status = client.argo.smart_routing.get(zone_id=zone_id)
|
||||
|
||||
if not status.editable:
|
||||
print('Cannot modify Argo: editable=false (check billing/permissions)')
|
||||
return False
|
||||
|
||||
if status.value == 'on':
|
||||
print('Argo already enabled')
|
||||
return True
|
||||
|
||||
client.argo.smart_routing.edit(zone_id=zone_id, value='on')
|
||||
print('Argo enabled successfully')
|
||||
return True
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
||||
The TypeScript SDK provides typed error classes for robust error handling:
|
||||
|
||||
```typescript
|
||||
import Cloudflare from 'cloudflare';
|
||||
import { APIError, APIConnectionError, RateLimitError } from 'cloudflare';
|
||||
|
||||
async function enableArgoWithErrorHandling(client: Cloudflare, zoneId: string) {
|
||||
try {
|
||||
const result = await client.argo.smartRouting.edit({
|
||||
zone_id: zoneId,
|
||||
value: 'on'
|
||||
});
|
||||
return result;
|
||||
} catch (error) {
|
||||
if (error instanceof RateLimitError) {
|
||||
console.error('Rate limited. Retry after:', error.response?.headers.get('retry-after'));
|
||||
// Implement exponential backoff
|
||||
} else if (error instanceof APIError) {
|
||||
console.error('API error:', error.status, error.message);
|
||||
if (error.status === 403) {
|
||||
console.error('Permission denied - check API token scopes');
|
||||
} else if (error.status === 400) {
|
||||
console.error('Bad request - verify zone_id and payload');
|
||||
}
|
||||
} else if (error instanceof APIConnectionError) {
|
||||
console.error('Connection failed:', error.message);
|
||||
// Retry with exponential backoff
|
||||
} else {
|
||||
console.error('Unexpected error:', error);
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Python Error Handling:**
|
||||
|
||||
```python
|
||||
from cloudflare import Cloudflare, APIError, RateLimitError
|
||||
|
||||
def enable_argo_with_error_handling(client: Cloudflare, zone_id: str):
|
||||
try:
|
||||
result = client.argo.smart_routing.edit(zone_id=zone_id, value='on')
|
||||
return result
|
||||
except RateLimitError as e:
|
||||
print(f"Rate limited. Retry after: {e.response.headers.get('retry-after')}")
|
||||
raise
|
||||
except APIError as e:
|
||||
print(f"API error: {e.status} - {e.message}")
|
||||
if e.status == 403:
|
||||
print('Permission denied - check API token scopes')
|
||||
elif e.status == 400:
|
||||
print('Bad request - verify zone_id and payload')
|
||||
raise
|
||||
except Exception as e:
|
||||
print(f"Unexpected error: {e}")
|
||||
raise
|
||||
```
|
||||
|
||||
## Response Schema
|
||||
|
||||
All Argo Smart Routing API responses follow this structure:
|
||||
|
||||
```typescript
|
||||
interface ArgoSmartRoutingResponse {
|
||||
result: {
|
||||
id: 'smart_routing';
|
||||
value: 'on' | 'off';
|
||||
editable: boolean;
|
||||
modified_on: string; // ISO 8601 timestamp
|
||||
};
|
||||
success: boolean;
|
||||
errors: Array<{
|
||||
code: number;
|
||||
message: string;
|
||||
}>;
|
||||
messages: Array<string>;
|
||||
}
|
||||
```
|
||||
|
||||
## Key Response Fields
|
||||
|
||||
| Field | Type | Description |
|
||||
| ------------- | --------------- | ------------------------------------------------ |
|
||||
| `value` | `"on" \| "off"` | Current enablement status |
|
||||
| `editable` | `boolean` | Whether changes are allowed (check before PATCH) |
|
||||
| `modified_on` | `string` | ISO timestamp of last modification |
|
||||
| `success` | `boolean` | Whether request succeeded |
|
||||
| `errors` | `Array` | Error details if `success: false` |
|
||||
+205
@@ -0,0 +1,205 @@
|
||||
## Configuration Management
|
||||
|
||||
**Note on Smart Shield Evolution:** Argo Smart Routing is being integrated into Smart Shield. Configuration methods below remain valid; Terraform and IaC patterns unchanged.
|
||||
|
||||
### Infrastructure as Code (Terraform)
|
||||
|
||||
```hcl
|
||||
# terraform/argo.tf
|
||||
# Note: Use Cloudflare Terraform provider
|
||||
|
||||
resource "cloudflare_argo" "example" {
|
||||
zone_id = var.zone_id
|
||||
smart_routing = "on"
|
||||
tiered_caching = "on"
|
||||
}
|
||||
|
||||
variable "zone_id" {
|
||||
description = "Cloudflare Zone ID"
|
||||
type = string
|
||||
}
|
||||
|
||||
output "argo_enabled" {
|
||||
value = cloudflare_argo.example.smart_routing
|
||||
description = "Argo Smart Routing status"
|
||||
}
|
||||
```
|
||||
|
||||
### Environment-Based Configuration
|
||||
|
||||
```typescript
|
||||
// config/argo.ts
|
||||
interface ArgoEnvironmentConfig {
|
||||
enabled: boolean;
|
||||
tieredCache: boolean;
|
||||
monitoring: {
|
||||
usageAlerts: boolean;
|
||||
threshold: number;
|
||||
};
|
||||
}
|
||||
|
||||
const configs: Record<string, ArgoEnvironmentConfig> = {
|
||||
production: {
|
||||
enabled: true,
|
||||
tieredCache: true,
|
||||
monitoring: {
|
||||
usageAlerts: true,
|
||||
threshold: 1000 // GB
|
||||
}
|
||||
},
|
||||
staging: {
|
||||
enabled: true,
|
||||
tieredCache: false,
|
||||
monitoring: {
|
||||
usageAlerts: false,
|
||||
threshold: 100 // GB
|
||||
}
|
||||
},
|
||||
development: {
|
||||
enabled: false,
|
||||
tieredCache: false,
|
||||
monitoring: {
|
||||
usageAlerts: false,
|
||||
threshold: 0
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export function getArgoConfig(env: string): ArgoEnvironmentConfig {
|
||||
return configs[env] || configs.development;
|
||||
}
|
||||
```
|
||||
|
||||
### Pulumi Configuration
|
||||
|
||||
```typescript
|
||||
// pulumi/argo.ts
|
||||
import * as cloudflare from '@pulumi/cloudflare';
|
||||
|
||||
const zone = new cloudflare.Zone('example-zone', {
|
||||
zone: 'example.com',
|
||||
plan: 'enterprise'
|
||||
});
|
||||
|
||||
const argoSettings = new cloudflare.Argo('argo-config', {
|
||||
zoneId: zone.id,
|
||||
smartRouting: 'on',
|
||||
tieredCaching: 'on'
|
||||
});
|
||||
|
||||
export const argoEnabled = argoSettings.smartRouting;
|
||||
export const zoneId = zone.id;
|
||||
```
|
||||
|
||||
## Billing Configuration
|
||||
|
||||
Before enabling Argo Smart Routing, ensure billing is configured for the account:
|
||||
|
||||
**Prerequisites:**
|
||||
|
||||
1. Valid payment method on file
|
||||
2. Enterprise or higher plan
|
||||
3. Zone must have billing enabled
|
||||
|
||||
**Check Billing Status via Dashboard:**
|
||||
|
||||
1. Navigate to Account → Billing
|
||||
2. Verify payment method configured
|
||||
3. Check zone subscription status
|
||||
|
||||
**Note:** Attempting to enable Argo without billing configured will result in `editable: false` in API responses.
|
||||
|
||||
## Environment Variable Setup
|
||||
|
||||
**Required Environment Variables:**
|
||||
|
||||
```bash
|
||||
# .env
|
||||
CLOUDFLARE_API_TOKEN=your_api_token_here
|
||||
CLOUDFLARE_ZONE_ID=your_zone_id_here
|
||||
CLOUDFLARE_ACCOUNT_ID=your_account_id_here
|
||||
|
||||
# Optional
|
||||
ARGO_ENABLED=true
|
||||
ARGO_TIERED_CACHE=true
|
||||
```
|
||||
|
||||
**TypeScript Configuration Loader:**
|
||||
|
||||
```typescript
|
||||
// config/env.ts
|
||||
import { z } from 'zod';
|
||||
|
||||
const envSchema = z.object({
|
||||
CLOUDFLARE_API_TOKEN: z.string().min(1),
|
||||
CLOUDFLARE_ZONE_ID: z.string().min(1),
|
||||
CLOUDFLARE_ACCOUNT_ID: z.string().min(1),
|
||||
ARGO_ENABLED: z.string().optional().default('false'),
|
||||
ARGO_TIERED_CACHE: z.string().optional().default('false')
|
||||
});
|
||||
|
||||
export const env = envSchema.parse(process.env);
|
||||
|
||||
export const argoConfig = {
|
||||
enabled: env.ARGO_ENABLED === 'true',
|
||||
tieredCache: env.ARGO_TIERED_CACHE === 'true'
|
||||
};
|
||||
```
|
||||
|
||||
## CI/CD Integration
|
||||
|
||||
**GitHub Actions Example:**
|
||||
|
||||
```yaml
|
||||
# .github/workflows/deploy-argo.yml
|
||||
name: Deploy Argo Configuration
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
paths:
|
||||
- 'terraform/argo.tf'
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Setup Terraform
|
||||
uses: hashicorp/setup-terraform@v2
|
||||
|
||||
- name: Terraform Init
|
||||
run: terraform init
|
||||
working-directory: ./terraform
|
||||
|
||||
- name: Terraform Apply
|
||||
run: terraform apply -auto-approve
|
||||
working-directory: ./terraform
|
||||
env:
|
||||
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
||||
TF_VAR_zone_id: ${{ secrets.CLOUDFLARE_ZONE_ID }}
|
||||
```
|
||||
|
||||
## Enterprise Preview Program
|
||||
|
||||
For early access to Argo Smart Routing features and Smart Shield integration:
|
||||
|
||||
**Eligibility:**
|
||||
|
||||
- Enterprise plan customers
|
||||
- Active Cloudflare support contract
|
||||
- Production traffic >100GB/month
|
||||
|
||||
**How to Join:**
|
||||
|
||||
1. Contact Cloudflare account team or support
|
||||
2. Request Argo/Smart Shield preview access
|
||||
3. Receive preview zone configuration
|
||||
|
||||
**Preview Features:**
|
||||
|
||||
- Enhanced analytics and reporting
|
||||
- Smart Shield DDoS integration
|
||||
- Advanced routing policies
|
||||
- Priority support for routing issues
|
||||
+115
@@ -0,0 +1,115 @@
|
||||
## Best Practices Summary
|
||||
|
||||
**Smart Shield Note:** Argo Smart Routing evolving into Smart Shield. Best practices below remain applicable; monitor Cloudflare changelog for Smart Shield updates.
|
||||
|
||||
1. **Always check editability** before attempting to enable/disable Argo
|
||||
2. **Set up billing notifications** to avoid unexpected costs
|
||||
3. **Combine with Tiered Cache** for maximum performance benefit
|
||||
4. **Use in production only** - disable for dev/staging to control costs
|
||||
5. **Monitor analytics** - require 500+ requests in 48h for detailed metrics
|
||||
6. **Handle errors gracefully** - check for billing, permissions, zone compatibility
|
||||
7. **Test configuration changes** in staging before production
|
||||
8. **Use TypeScript SDK** for type safety and better developer experience
|
||||
9. **Implement retry logic** for API calls in production systems
|
||||
10. **Document zone-specific settings** for team visibility
|
||||
|
||||
## Common Errors
|
||||
|
||||
### "Argo unavailable"
|
||||
|
||||
**Problem:** API returns error "Argo Smart Routing is unavailable for this zone"
|
||||
|
||||
**Cause:** Zone not eligible or billing not set up
|
||||
|
||||
**Solution:**
|
||||
|
||||
1. Verify zone has Enterprise or higher plan
|
||||
2. Check billing is configured in Account → Billing
|
||||
3. Ensure payment method is valid and current
|
||||
4. Contact Cloudflare support if eligibility unclear
|
||||
|
||||
### "Cannot enable/disable"
|
||||
|
||||
**Problem:** API call succeeds but status remains unchanged, or `editable: false` in GET response
|
||||
|
||||
**Cause:** Insufficient permissions or zone restrictions
|
||||
|
||||
**Solution:**
|
||||
|
||||
1. Check API token has `Zone:Argo Smart Routing:Edit` permission
|
||||
2. Verify `editable: true` in GET response before attempting PATCH
|
||||
3. If `editable: false`, check:
|
||||
- Billing configured for account
|
||||
- Zone plan includes Argo (Enterprise+)
|
||||
- No active zone holds or suspensions
|
||||
- API token has correct scopes
|
||||
|
||||
### `editable: false` Error
|
||||
|
||||
**Problem:** GET request returns `"editable": false`, preventing enable/disable
|
||||
|
||||
**Cause:** Zone-level restrictions from billing, plan, or permissions
|
||||
|
||||
**Solution Pattern:**
|
||||
|
||||
```typescript
|
||||
const status = await client.argo.smartRouting.get({ zone_id: zoneId });
|
||||
|
||||
if (!status.editable) {
|
||||
// Don't attempt to modify - will fail
|
||||
console.error('Cannot modify Argo settings:');
|
||||
console.error('- Check billing is configured');
|
||||
console.error('- Verify zone has Enterprise+ plan');
|
||||
console.error('- Confirm API token has Edit permission');
|
||||
throw new Error('Argo is not editable for this zone');
|
||||
}
|
||||
|
||||
// Safe to proceed with enable/disable
|
||||
await client.argo.smartRouting.edit({ zone_id: zoneId, value: 'on' });
|
||||
```
|
||||
|
||||
### Rate Limiting
|
||||
|
||||
**Problem:** `429 Too Many Requests` error from API
|
||||
|
||||
**Cause:** Exceeded API rate limits (typically 1200 requests per 5 minutes)
|
||||
|
||||
**Solution:**
|
||||
|
||||
```typescript
|
||||
import { RateLimitError } from 'cloudflare';
|
||||
|
||||
try {
|
||||
await client.argo.smartRouting.edit({ zone_id: zoneId, value: 'on' });
|
||||
} catch (error) {
|
||||
if (error instanceof RateLimitError) {
|
||||
const retryAfter = error.response?.headers.get('retry-after');
|
||||
console.log(`Rate limited. Retry after ${retryAfter} seconds`);
|
||||
|
||||
// Implement exponential backoff
|
||||
await new Promise((resolve) => setTimeout(resolve, (retryAfter || 60) * 1000));
|
||||
// Retry request
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Limits
|
||||
|
||||
| Resource/Limit | Value | Notes |
|
||||
| -------------------------- | ------------------ | --------------------------------------- |
|
||||
| Min requests for analytics | 500 in 48h | For detailed metrics via GraphQL |
|
||||
| Zones supported | Enterprise+ | Check zone plan in dashboard |
|
||||
| Billing requirement | Must be configured | Before enabling; verify payment method |
|
||||
| API rate limit | 1200 req / 5 min | Per API token across all endpoints |
|
||||
| Spectrum apps | No hard limit | Each app can enable Argo independently |
|
||||
| Traffic counting | Proxied only | Only orange-clouded DNS records count |
|
||||
| DDoS/WAF exemption | Yes | Mitigated traffic excluded from billing |
|
||||
| Analytics latency | 1-5 minutes | Real-time metrics not available |
|
||||
|
||||
## Additional Resources
|
||||
|
||||
- [Official Argo Smart Routing Docs](https://developers.cloudflare.com/argo-smart-routing/)
|
||||
- [Cloudflare Smart Shield](https://developers.cloudflare.com/smart-shield/)
|
||||
- [API Authentication](https://developers.cloudflare.com/fundamentals/api/get-started/create-token/)
|
||||
- [Cloudflare TypeScript SDK](https://github.com/cloudflare/cloudflare-typescript)
|
||||
- [Cloudflare Python SDK](https://github.com/cloudflare/cloudflare-python)
|
||||
+107
@@ -0,0 +1,107 @@
|
||||
# Integration Patterns
|
||||
|
||||
## Enable Argo + Tiered Cache
|
||||
|
||||
```typescript
|
||||
async function enableOptimalPerformance(client: Cloudflare, zoneId: string) {
|
||||
await Promise.all([
|
||||
client.argo.smartRouting.edit({ zone_id: zoneId, value: 'on' }),
|
||||
client.argo.tieredCaching.edit({ zone_id: zoneId, value: 'on' })
|
||||
]);
|
||||
}
|
||||
```
|
||||
|
||||
**Flow:** Visitor → Edge (Lower-Tier) → [Cache Miss] → Upper-Tier → [Cache Miss + Argo] → Origin
|
||||
|
||||
**Impact:** Argo ~30% latency reduction + Tiered Cache 50-80% origin offload
|
||||
|
||||
## Usage Analytics (GraphQL)
|
||||
|
||||
```graphql
|
||||
query ArgoAnalytics($zoneTag: string!) {
|
||||
viewer {
|
||||
zones(filter: { zoneTag: $zoneTag }) {
|
||||
httpRequestsAdaptiveGroups(limit: 1000) {
|
||||
sum {
|
||||
argoBytes
|
||||
bytes
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Billing:** ~$0.10/GB. DDoS-mitigated and WAF-blocked traffic NOT charged.
|
||||
|
||||
## Spectrum TCP Integration
|
||||
|
||||
Enable Argo for non-HTTP traffic (databases, game servers, IoT):
|
||||
|
||||
```typescript
|
||||
// Update existing app
|
||||
await client.spectrum.apps.update(appId, { zone_id: zoneId, argo_smart_routing: true });
|
||||
|
||||
// Create new app with Argo
|
||||
await client.spectrum.apps.create({
|
||||
zone_id: zoneId,
|
||||
dns: { type: 'CNAME', name: 'tcp.example.com' },
|
||||
origin_direct: ['tcp://origin.example.com:3306'],
|
||||
protocol: 'tcp/3306',
|
||||
argo_smart_routing: true
|
||||
});
|
||||
```
|
||||
|
||||
**Use cases:** MySQL/PostgreSQL (3306/5432), game servers, MQTT (1883), SSH (22)
|
||||
|
||||
## Pre-Flight Validation
|
||||
|
||||
```typescript
|
||||
async function validateArgoEligibility(client: Cloudflare, zoneId: string) {
|
||||
const status = await client.argo.smartRouting.get({ zone_id: zoneId });
|
||||
const zone = await client.zones.get({ zone_id: zoneId });
|
||||
|
||||
const issues: string[] = [];
|
||||
if (!status.editable) issues.push('Zone not editable');
|
||||
if (['free', 'pro'].includes(zone.plan.legacy_id)) issues.push('Requires Business+ plan');
|
||||
if (zone.status !== 'active') issues.push('Zone not active');
|
||||
|
||||
return { canEnable: issues.length === 0, issues };
|
||||
}
|
||||
```
|
||||
|
||||
## Post-Enable Verification
|
||||
|
||||
```typescript
|
||||
async function verifyArgoEnabled(client: Cloudflare, zoneId: string): Promise<boolean> {
|
||||
await new Promise((r) => setTimeout(r, 2000)); // Wait for propagation
|
||||
const status = await client.argo.smartRouting.get({ zone_id: zoneId });
|
||||
return status.value === 'on';
|
||||
}
|
||||
```
|
||||
|
||||
## Full Setup Pattern
|
||||
|
||||
```typescript
|
||||
async function setupArgo(client: Cloudflare, zoneId: string) {
|
||||
// 1. Validate
|
||||
const { canEnable, issues } = await validateArgoEligibility(client, zoneId);
|
||||
if (!canEnable) throw new Error(issues.join(', '));
|
||||
|
||||
// 2. Enable both features
|
||||
await Promise.all([
|
||||
client.argo.smartRouting.edit({ zone_id: zoneId, value: 'on' }),
|
||||
client.argo.tieredCaching.edit({ zone_id: zoneId, value: 'on' })
|
||||
]);
|
||||
|
||||
// 3. Verify
|
||||
const [argo, cache] = await Promise.all([
|
||||
client.argo.smartRouting.get({ zone_id: zoneId }),
|
||||
client.argo.tieredCaching.get({ zone_id: zoneId })
|
||||
]);
|
||||
|
||||
return { argo: argo.value === 'on', tieredCache: cache.value === 'on' };
|
||||
}
|
||||
```
|
||||
|
||||
**When to combine:** High-traffic sites (>1TB/mo), global users, cacheable content.
|
||||
@@ -0,0 +1,80 @@
|
||||
# Cloudflare Artifacts
|
||||
|
||||
Store versioned file trees behind a repo-style interface that works from Workers, the REST API, and Git-compatible tooling.
|
||||
|
||||
## Overview
|
||||
|
||||
Use **Artifacts** when the thing you need to store is a versioned filesystem tree rather than a single object, key, or SQL row.
|
||||
|
||||
Typical Artifacts use cases:
|
||||
|
||||
- Git-style repositories
|
||||
- Per-agent, per-session, or per-task repos
|
||||
- Build outputs and deployment bundles
|
||||
- Checkpoints and generated assets
|
||||
- Shared file trees passed between developer tools and Workers
|
||||
|
||||
Artifacts is a good fit when the same content needs to be addressable from **Workers**, the **REST API**, and **Git-compatible clients**.
|
||||
|
||||
Artifacts is especially useful for agent and automation workflows where each unit of work should have its own isolated repo and token.
|
||||
|
||||
**Prefer retrieval over memory** for current availability, authentication details, route shapes, limits, and pricing. Start at `https://developers.cloudflare.com/artifacts/`.
|
||||
|
||||
## When to Use Artifacts
|
||||
|
||||
| Need | Use | Why |
|
||||
| ----------------------------------------------------------------------------------- | ------------- | ----------------------------------------------------------------------------------------------------------------- |
|
||||
| Versioned file trees such as repos, build outputs, checkpoints, or generated assets | Artifacts | Artifacts stores and shares **versioned filesystem content** |
|
||||
| A git-compatible workflow with `clone`, `fetch`, `pull`, or `push` | Artifacts | Artifacts exposes **git-over-HTTPS remotes** and repo-scoped tokens |
|
||||
| The same artifact accessible from Workers, HTTP APIs, and developer tooling | Artifacts | Artifacts is available through a **Workers binding**, **REST API**, and **git-compatible interface** |
|
||||
| Large files by object key, app config by key, or relational app data | R2, KV, or D1 | Use storage products directly when you need **objects, key-value entries, or SQL rows**, not versioned file trees |
|
||||
|
||||
## Recommended Workflow
|
||||
|
||||
- Create one repo per agent, session, user workspace, or task when work should stay isolated.
|
||||
- Fork from a stable baseline when many repos need the same starter files or prompts.
|
||||
- Use branches only when collaborators share the same lifecycle and need to work in one repo.
|
||||
- Use namespaces to separate environments, teams, or high-rate workloads.
|
||||
|
||||
## Quick Start
|
||||
|
||||
**From a Worker:**
|
||||
|
||||
```typescript
|
||||
interface Env {
|
||||
ARTIFACTS: Artifacts;
|
||||
}
|
||||
|
||||
const created = await env.ARTIFACTS.create('starter-repo');
|
||||
// created.remote -> git remote URL
|
||||
// created.token -> initial repo token
|
||||
```
|
||||
|
||||
**From the REST API:**
|
||||
|
||||
Use the namespace-scoped Artifacts base URL plus a gateway JWT. For imports from existing HTTPS remotes, use the REST API rather than the Workers binding.
|
||||
|
||||
## Reading Order
|
||||
|
||||
| Task | Read |
|
||||
| --------------------------------------------- | --------------------------------------------------------------- |
|
||||
| Decide whether Artifacts is the right product | README only |
|
||||
| Create or manage repos from a Worker | README → configuration.md → api.md |
|
||||
| Integrate Artifacts from an external system | README → api.md |
|
||||
| Set up agent or sandbox workflows | README → configuration.md |
|
||||
| Verify exact auth, routes, limits, or pricing | Live docs first: `https://developers.cloudflare.com/artifacts/` |
|
||||
|
||||
## In This Reference
|
||||
|
||||
- **[api.md](api.md)** - Workers binding methods, REST routes, token and repo operations
|
||||
- **[configuration.md](configuration.md)** - Wrangler binding shape, Worker typing, REST configuration guidance
|
||||
|
||||
## See Also
|
||||
|
||||
- [Cloudflare Artifacts Docs](https://developers.cloudflare.com/artifacts/)
|
||||
- [Artifacts Git Protocol Docs](https://developers.cloudflare.com/artifacts/api/git-protocol/)
|
||||
- [ArtifactFS Docs](https://developers.cloudflare.com/artifacts/guides/artifact-fs/)
|
||||
- [Cloudflare Workers Docs](https://developers.cloudflare.com/workers/)
|
||||
- [Cloudflare Durable Objects Docs](https://developers.cloudflare.com/durable-objects/)
|
||||
- [Cloudflare R2 Docs](https://developers.cloudflare.com/r2/)
|
||||
- [Cloudflare D1 Docs](https://developers.cloudflare.com/d1/)
|
||||
@@ -0,0 +1,129 @@
|
||||
# Artifacts API Reference
|
||||
|
||||
Use Artifacts through the **Workers binding**, the **REST control plane**, and **Git-compatible remotes**.
|
||||
|
||||
**Prefer retrieval** for exact request and response details. Verify current behavior at `https://developers.cloudflare.com/artifacts/` before relying on specific auth flows, route details, or generated binding types.
|
||||
|
||||
## Workers Binding
|
||||
|
||||
Artifacts exposes a Worker binding on `env.ARTIFACTS`.
|
||||
|
||||
### Namespace Methods
|
||||
|
||||
| Method | Use For |
|
||||
| --------------------- | ------------------------------------------------------ |
|
||||
| `create(name, opts?)` | Create a repo and receive its initial remote and token |
|
||||
| `get(name)` | Resolve a repo handle for repo-scoped operations |
|
||||
| `list(opts?)` | List repos in a namespace |
|
||||
| `delete(name)` | Delete a repo |
|
||||
|
||||
```typescript
|
||||
const created = await env.ARTIFACTS.create('starter-repo', {
|
||||
description: 'Repository for automation experiments',
|
||||
setDefaultBranch: 'main'
|
||||
});
|
||||
const repo = await env.ARTIFACTS.get('starter-repo');
|
||||
const page = await env.ARTIFACTS.list({ limit: 10 });
|
||||
```
|
||||
|
||||
Use the REST API when you need to import a repo from another HTTPS remote.
|
||||
|
||||
### Repo Handle Methods
|
||||
|
||||
Use a repo handle returned by `get()` or `create()`.
|
||||
|
||||
| Method | Use For |
|
||||
| --------------------------- | -------------------------------------------- |
|
||||
| `info()` | Read repo metadata, including the remote URL |
|
||||
| `createToken(scope?, ttl?)` | Mint a repo-scoped read or write token |
|
||||
| `listTokens()` | Inspect active tokens |
|
||||
| `validateToken(token)` | Check whether a token is still valid |
|
||||
| `revokeToken(tokenOrId)` | Revoke a token by ID or value |
|
||||
| `fork(name, opts?)` | Fork one repo into another |
|
||||
|
||||
```typescript
|
||||
const repo = await env.ARTIFACTS.get('starter-repo');
|
||||
if (!repo) throw new Error('Repo not found');
|
||||
|
||||
const info = await repo.info();
|
||||
const token = await repo.createToken('read', 3600);
|
||||
const forked = await repo.fork('starter-repo-copy', {
|
||||
defaultBranchOnly: true
|
||||
});
|
||||
```
|
||||
|
||||
### Binding Notes
|
||||
|
||||
- Current docs describe the runtime binding surface as `create`, `get`, `list`, `delete`, and repo-handle methods like `info`, `createToken`, and `fork`.
|
||||
- Use `npx wrangler types` in the target project and treat the generated `worker-configuration.d.ts` as the source of truth for that environment.
|
||||
- If generated types appear to expose `import()` or a different `get()` shape, verify the live docs before depending on those methods.
|
||||
|
||||
Verify current runtime behavior in the live docs before depending on methods that are not shown in the Workers binding reference.
|
||||
|
||||
## REST API
|
||||
|
||||
Artifacts currently documents a namespace-scoped control plane:
|
||||
|
||||
```txt
|
||||
https://artifacts.cloudflare.net/v1/api/namespaces/$ARTIFACTS_NAMESPACE
|
||||
```
|
||||
|
||||
Some deployments also expose an `/edge/v1/api/...` base path. Verify the correct base URL for your environment in the live docs.
|
||||
|
||||
Requests to the standard `/v1/api/...` routes use a **gateway JWT** with Bearer authentication.
|
||||
|
||||
Returned repo tokens authenticate **Git operations** against the repo `remote`. They do not authenticate REST control-plane requests.
|
||||
|
||||
Current docs show the standard Cloudflare v4 response envelope around REST results.
|
||||
|
||||
### Repo Routes
|
||||
|
||||
| Route | Use For |
|
||||
| -------------------------- | ----------------------------- |
|
||||
| `POST /repos` | Create a repo |
|
||||
| `GET /repos` | List repos |
|
||||
| `GET /repos/:name` | Read repo metadata and remote |
|
||||
| `DELETE /repos/:name` | Delete a repo |
|
||||
| `POST /repos/:name/fork` | Fork a repo |
|
||||
| `POST /repos/:name/import` | Import a public HTTPS remote |
|
||||
|
||||
```bash
|
||||
curl --request POST "$ARTIFACTS_BASE_URL/repos" \
|
||||
--header "Authorization: Bearer $ARTIFACTS_JWT" \
|
||||
--header "Content-Type: application/json" \
|
||||
--data '{"name":"starter-repo"}'
|
||||
```
|
||||
|
||||
Important current details from the docs draft:
|
||||
|
||||
- `POST /repos/:name/import` accepts a full HTTPS remote URL such as GitHub or GitLab.
|
||||
- Import supports options such as `branch`, `depth`, and `read_only`.
|
||||
- Repo metadata includes fields such as description, default branch, timestamps, and the Git `remote`.
|
||||
|
||||
### Token Routes
|
||||
|
||||
| Route | Use For |
|
||||
| ------------------------- | ------------------------- |
|
||||
| `GET /repos/:name/tokens` | List repo tokens |
|
||||
| `POST /tokens` | Create a token for a repo |
|
||||
| `DELETE /tokens/:id` | Revoke a token by ID |
|
||||
|
||||
Current docs show list-token filtering and pagination by token state. Retrieve the exact query shape from the live docs when you need token audit or cleanup workflows.
|
||||
|
||||
Use **read** tokens for clone, fetch, pull, and indexing workflows. Use **write** tokens only when a workflow must push or otherwise mutate a repo.
|
||||
|
||||
## Git-Compatible Access
|
||||
|
||||
Artifacts returns repo `remote` URLs that work with standard git-over-HTTPS tooling.
|
||||
|
||||
Recommended current auth pattern for local workflows:
|
||||
|
||||
```bash
|
||||
git -c http.extraHeader="Authorization: Bearer $ARTIFACTS_TOKEN" clone "$ARTIFACTS_REMOTE" artifacts-clone
|
||||
```
|
||||
|
||||
Use a self-contained Basic-auth remote only for short-lived commands that need credentials embedded in the URL.
|
||||
|
||||
`read` tokens support `clone`, `fetch`, and `pull`. `git push` requires a `write` token.
|
||||
|
||||
For large repos where startup time matters more than a full clone, Artifacts also documents **ArtifactFS**. Retrieve current details from `https://developers.cloudflare.com/artifacts/` when you need mount-style access.
|
||||
@@ -0,0 +1,96 @@
|
||||
# Artifacts Configuration
|
||||
|
||||
## Worker Binding
|
||||
|
||||
Configure the `artifacts` binding in your Wrangler config:
|
||||
|
||||
```toml
|
||||
[[artifacts]]
|
||||
binding = "ARTIFACTS"
|
||||
namespace = "default"
|
||||
```
|
||||
|
||||
This exposes Artifacts on `env.ARTIFACTS` inside your Worker.
|
||||
|
||||
If you authenticate with `wrangler login`, current docs say Wrangler requests `artifacts:write` by default.
|
||||
|
||||
## TypeScript
|
||||
|
||||
Regenerate Worker types after adding the binding:
|
||||
|
||||
```bash
|
||||
npx wrangler types
|
||||
```
|
||||
|
||||
Use the generated binding type in your environment definition:
|
||||
|
||||
```typescript
|
||||
interface Env {
|
||||
ARTIFACTS: Artifacts;
|
||||
}
|
||||
```
|
||||
|
||||
Wrangler generates the `Artifacts` type from the binding. Treat the generated `worker-configuration.d.ts` file as the source of truth for your environment.
|
||||
|
||||
## Structure Repos for Isolation
|
||||
|
||||
Artifacts works best when autonomous work is isolated:
|
||||
|
||||
- Create one repo per agent, session, sandbox, or task when work should stay separate.
|
||||
- Fork from a reviewed baseline instead of copying starter files into every new repo.
|
||||
- Use branches only when collaborators share the same lifecycle and need to work in one repo.
|
||||
- Use namespaces to separate environments, teams, or high-rate workloads.
|
||||
|
||||
## REST Configuration
|
||||
|
||||
For external systems, configure the namespace-scoped base URL and gateway JWT:
|
||||
|
||||
```bash
|
||||
export ARTIFACTS_NAMESPACE="default"
|
||||
export ARTIFACTS_JWT="<YOUR_GATEWAY_JWT>"
|
||||
export ARTIFACTS_BASE_URL="https://artifacts.cloudflare.net/v1/api/namespaces/$ARTIFACTS_NAMESPACE"
|
||||
```
|
||||
|
||||
Some environments also expose an `/edge/v1/api/...` base path. Verify the correct host and base path in the live docs for your Artifacts environment.
|
||||
|
||||
Use environment variables or your secret manager. Do not hardcode gateway JWTs or repo tokens.
|
||||
|
||||
## Repo Tokens
|
||||
|
||||
Artifacts workflows usually involve repo-scoped tokens returned by `create()` or minted later through the binding or REST API.
|
||||
|
||||
Keep the control plane and data plane separate:
|
||||
|
||||
- Use the **Workers binding** or **REST API** with a gateway JWT to create repos and mint tokens.
|
||||
- Use repo-scoped tokens only for **Git operations** against the returned `remote`.
|
||||
|
||||
Recommended handling:
|
||||
|
||||
- Mint the narrowest scope you need: `read` or `write`
|
||||
- Prefer short-lived tokens for handoff between systems
|
||||
- Revoke tokens that are no longer needed
|
||||
|
||||
Verify the current token behavior and auth guidance in `https://developers.cloudflare.com/artifacts/` before building long-lived automation.
|
||||
|
||||
## Git Consumers
|
||||
|
||||
Artifacts is designed to work with standard git-over-HTTPS clients once you have a repo `remote` and an access token.
|
||||
|
||||
Prefer header-based auth for local tooling so the full token stays out of the remote URL:
|
||||
|
||||
```bash
|
||||
git -c http.extraHeader="Authorization: Bearer $ARTIFACTS_TOKEN" clone "$ARTIFACTS_REMOTE" artifacts-clone
|
||||
```
|
||||
|
||||
Use a Basic-auth remote only for short-lived commands that need a self-contained URL.
|
||||
|
||||
## Retrieval Checklist
|
||||
|
||||
Check the live docs before relying on:
|
||||
|
||||
- the current Workers binding surface
|
||||
- exact token formats
|
||||
- availability or product status
|
||||
- route details for import, fork, and token-management flows
|
||||
- the correct control-plane host or `/edge/v1` base path for your environment
|
||||
- platform limits or pricing
|
||||
@@ -0,0 +1,16 @@
|
||||
# Cloudflare Workers Bindings
|
||||
|
||||
Bindings grant a Worker access to configured resources through its environment. Prefer a product's binding for supported operations inside Workers; use the REST API when the caller or operation requires it.
|
||||
|
||||
Read the relevant current documentation before implementing. These references route to maintained APIs and configuration rather than copying binding catalogs, type tables, or limits.
|
||||
|
||||
## Start here
|
||||
|
||||
- [Bindings overview and catalog](https://developers.cloudflare.com/workers/runtime-apis/bindings/): capability model, available products, environment access, and binding lifecycle.
|
||||
- [Storage options](https://developers.cloudflare.com/workers/platform/storage-options/): choose storage from consistency, query, and coordination requirements.
|
||||
- [api.md](./api.md): environment access, generated types, and product APIs.
|
||||
- [configuration.md](./configuration.md): binding configuration, environments, secrets, and local development.
|
||||
- [patterns.md](./patterns.md): Worker-to-Worker calls, testing, and resource selection.
|
||||
- [gotchas.md](./gotchas.md): missing bindings, stale clients, development differences, and limits.
|
||||
|
||||
Treat each binding as a capability granted to code. Select only the resources the Worker needs, and confirm which environment and resource each binding targets before using it.
|
||||
@@ -0,0 +1,19 @@
|
||||
# Bindings API Reference
|
||||
|
||||
Fetch the relevant documentation before choosing method signatures, binding types, or type-generation settings.
|
||||
|
||||
| Task | Current documentation |
|
||||
| -------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| Access bindings in handlers, entrypoint classes, or imported `env` | [Bindings and environment access](https://developers.cloudflare.com/workers/runtime-apis/bindings/) |
|
||||
| Generate environment and runtime types; configure TypeScript or migrate from `@cloudflare/workers-types` | [Workers TypeScript](https://developers.cloudflare.com/workers/languages/typescript/) |
|
||||
| Use framework-specific environment access | [Hono on Workers](https://developers.cloudflare.com/workers/framework-guides/web-apps/more-web-frameworks/hono/) (follow the framework's linked documentation) |
|
||||
| Read, write, delete, and list KV keys | [KV Workers API](https://developers.cloudflare.com/kv/api/) |
|
||||
| Read, write, delete, and list R2 objects | [R2 Workers API](https://developers.cloudflare.com/r2/api/workers/workers-api-reference/) |
|
||||
| Prepare and bind SQL statements or execute batches | [D1 Workers Binding API](https://developers.cloudflare.com/d1/worker-api/) |
|
||||
| Address Durable Objects and call their methods | [Durable Objects API](https://developers.cloudflare.com/durable-objects/api/) |
|
||||
| Send queue messages | [Queues JavaScript APIs](https://developers.cloudflare.com/queues/configuration/javascript-apis/) |
|
||||
| Run model inference | [Workers AI bindings](https://developers.cloudflare.com/workers-ai/configuration/bindings/) |
|
||||
| Call another Worker using HTTP or typed RPC | [Service bindings](https://developers.cloudflare.com/workers/runtime-apis/bindings/service-bindings/) and [RPC TypeScript](https://developers.cloudflare.com/workers/runtime-apis/rpc/typescript/) |
|
||||
| Find other product binding APIs, including Browser, mTLS, rate limiting, and Workflows | [Current binding catalog](https://developers.cloudflare.com/workers/runtime-apis/bindings/) |
|
||||
|
||||
Regenerate types after configuration changes and use the selected environment's configuration. Follow the current TypeScript setup for the project's toolchain instead of hardcoding a generated declaration path or maintaining a handwritten binding interface. Types describe the expected bindings; they do not provision resources or prove the deployed environment is configured correctly.
|
||||
@@ -0,0 +1,20 @@
|
||||
# Binding Configuration
|
||||
|
||||
Confirm the target account, environment, and resource before adding or changing a binding. Keep staging and production resources separate where their data or permissions must be isolated.
|
||||
|
||||
| Task | Current documentation |
|
||||
| ----------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| Configure storage, compute, platform, and service bindings | [Wrangler binding configuration](https://developers.cloudflare.com/workers/wrangler/configuration/#bindings) |
|
||||
| Create or inspect resources and manage deployments | [Wrangler commands](https://developers.cloudflare.com/workers/wrangler/commands/) (select the product's commands) |
|
||||
| Configure named environments and non-inheritable bindings | [Wrangler environments](https://developers.cloudflare.com/workers/wrangler/environments/) |
|
||||
| Set non-sensitive configuration | [Environment variables](https://developers.cloudflare.com/workers/configuration/environment-variables/) |
|
||||
| Set or rotate credentials in a chosen environment | [Secrets](https://developers.cloudflare.com/workers/configuration/secrets/) |
|
||||
| Configure text, data, and Wasm modules in existing projects | [Wrangler bundling](https://developers.cloudflare.com/workers/wrangler/bundling/) and [configuration](https://developers.cloudflare.com/workers/wrangler/configuration/) |
|
||||
| Choose locally simulated resources or remote bindings | [Local development](https://developers.cloudflare.com/workers/local-development/) and [supported bindings per development mode](https://developers.cloudflare.com/workers/local-development/bindings-per-env/) |
|
||||
| Supply local variables and secrets | [Local environment variables and secrets](https://developers.cloudflare.com/workers/local-development/environment-variables/) |
|
||||
| Seed or persist local resource data | [Adding local data](https://developers.cloudflare.com/workers/local-development/local-data/) |
|
||||
| Generate types after configuring bindings | [Workers TypeScript](https://developers.cloudflare.com/workers/languages/typescript/) |
|
||||
|
||||
Bindings and variables are not inherited automatically by named environments. Configure the required values for the environment being used, including its secrets. Binding names used in code must match configuration; the target resource's ID or name is a separate value.
|
||||
|
||||
Keep credentials out of committed variables and local secret files out of version control. Remote development can access real resources: verify the target rather than assuming that running locally isolates writes.
|
||||
@@ -0,0 +1,18 @@
|
||||
# Binding Gotchas and Troubleshooting
|
||||
|
||||
Start by checking the binding name, selected environment, and actual target resource. Regenerating types alone does not fix a missing runtime binding.
|
||||
|
||||
| Symptom or question | What to check |
|
||||
| ----------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| A binding is undefined or points to unexpected data | [Binding configuration](https://developers.cloudflare.com/workers/wrangler/configuration/#bindings) and [environment inheritance](https://developers.cloudflare.com/workers/wrangler/environments/) |
|
||||
| TypeScript cannot find a binding or runtime type | [Current type generation and TypeScript setup](https://developers.cloudflare.com/workers/languages/typescript/) |
|
||||
| Updated credentials are not reflected in a client | [Binding changes and retained global state](https://developers.cloudflare.com/workers/runtime-apis/bindings/#making-changes-to-bindings); verify the [secret's environment and deployment](https://developers.cloudflare.com/workers/configuration/secrets/) |
|
||||
| Binding calls fail outside a handler | [Environment access and global-scope restrictions](https://developers.cloudflare.com/workers/runtime-apis/bindings/#how-to-access-env) |
|
||||
| A service target is unavailable or incorrect | [Service bindings](https://developers.cloudflare.com/workers/runtime-apis/bindings/service-bindings/) and [multi-Worker development](https://developers.cloudflare.com/workers/local-development/multi-workers/) |
|
||||
| Local data, secrets, or available bindings differ from deployment | [Development mode support](https://developers.cloudflare.com/workers/local-development/bindings-per-env/), [local data](https://developers.cloudflare.com/workers/local-development/local-data/), and [local secrets](https://developers.cloudflare.com/workers/local-development/environment-variables/) |
|
||||
| KV reads appear stale or return no value | [How KV works](https://developers.cloudflare.com/kv/concepts/how-kv-works/); verify the namespace and handle missing values |
|
||||
| Resource-specific API errors or limits | Follow the product from the [binding catalog](https://developers.cloudflare.com/workers/runtime-apis/bindings/) to its troubleshooting and limits documentation |
|
||||
| Worker resource limits or unexpected charges | [Workers limits](https://developers.cloudflare.com/workers/platform/limits/) and [pricing](https://developers.cloudflare.com/workers/platform/pricing/); check each bound product's limits and pricing separately |
|
||||
| Need to inspect configuration, resources, deployments, or logs | [Wrangler commands](https://developers.cloudflare.com/workers/wrangler/commands/) and [real-time logs](https://developers.cloudflare.com/workers/observability/logs/real-time-logs/) |
|
||||
|
||||
Never log secret values or return the environment object in a response. Inspect names and configuration without exposing credentials. Check the selected resource and development mode before issuing debugging commands that could mutate remote data.
|
||||
@@ -0,0 +1,20 @@
|
||||
# Binding Patterns
|
||||
|
||||
Choose the interaction and lifecycle first, then retrieve the implementation guide.
|
||||
|
||||
| Task | Current documentation |
|
||||
| --------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| Choose HTTP forwarding or RPC between Workers | [Service bindings](https://developers.cloudflare.com/workers/runtime-apis/bindings/service-bindings/) |
|
||||
| Forward Requests and Responses through a service binding | [Service bindings over HTTP](https://developers.cloudflare.com/workers/runtime-apis/bindings/service-bindings/http/) |
|
||||
| Expose callable methods with `WorkerEntrypoint` | [Service bindings over RPC](https://developers.cloudflare.com/workers/runtime-apis/bindings/service-bindings/rpc/) and [RPC TypeScript](https://developers.cloudflare.com/workers/runtime-apis/rpc/typescript/) |
|
||||
| Run connected Workers during development | [Developing with multiple Workers](https://developers.cloudflare.com/workers/local-development/multi-workers/) |
|
||||
| Test handlers against configured bindings and mock dependencies | [Workers Vitest configuration](https://developers.cloudflare.com/workers/testing/vitest-integration/configuration/) and [test APIs](https://developers.cloudflare.com/workers/testing/vitest-integration/test-apis/) |
|
||||
| Select KV, D1, R2, or Durable Objects | [Storage options](https://developers.cloudflare.com/workers/platform/storage-options/) |
|
||||
| Keep clients current when bindings change | [Binding lifecycle](https://developers.cloudflare.com/workers/runtime-apis/bindings/#making-changes-to-bindings) |
|
||||
| Manage credentials used by external API clients | [Secrets](https://developers.cloudflare.com/workers/configuration/secrets/) |
|
||||
|
||||
Use service bindings for internal Worker calls when appropriate, and choose HTTP or RPC based on the interface being exposed. A service binding does not replace application-level authorization for the caller's requested operation.
|
||||
|
||||
Choose storage based on access patterns and consistency requirements, not copied size or latency thresholds. Parallelize independent binding operations when useful; preserve ordering where one operation depends on another's result.
|
||||
|
||||
Avoid retaining clients derived from mutable bindings across requests without accounting for binding updates. Importing `env` is supported, but binding I/O still requires an appropriate execution context; follow the lifecycle guide rather than assuming all global access is forbidden.
|
||||
@@ -0,0 +1,95 @@
|
||||
# Cloudflare Bot Management
|
||||
|
||||
Enterprise-grade bot detection, protection, and mitigation using ML/heuristics, bot scores, JavaScript detections, and verified bot handling.
|
||||
|
||||
## Overview
|
||||
|
||||
Bot Management provides multi-tier protection:
|
||||
|
||||
- **Free (Bot Fight Mode)**: Auto-blocks definite bots, no config
|
||||
- **Pro/Business (Super Bot Fight Mode)**: Configurable actions, static resource protection, analytics groupings
|
||||
- **Enterprise (Bot Management)**: Granular 1-99 scores, WAF integration, JA3/JA4 fingerprinting, Workers API, Advanced Analytics
|
||||
|
||||
## Quick Start
|
||||
|
||||
```txt
|
||||
# Dashboard: Security > Bots
|
||||
# Enterprise: Deploy rule template
|
||||
(cf.bot_management.score eq 1 and not cf.bot_management.verified_bot) → Block
|
||||
(cf.bot_management.score le 29 and not cf.bot_management.verified_bot) → Managed Challenge
|
||||
```
|
||||
|
||||
## What Do You Need?
|
||||
|
||||
```txt
|
||||
├─ Initial setup → configuration.md
|
||||
│ ├─ Free tier → "Bot Fight Mode"
|
||||
│ ├─ Pro/Business → "Super Bot Fight Mode"
|
||||
│ └─ Enterprise → "Bot Management for Enterprise"
|
||||
├─ Workers API integration → api.md
|
||||
├─ WAF rules → patterns.md
|
||||
├─ Debugging → gotchas.md
|
||||
└─ Analytics → api.md#bot-analytics
|
||||
```
|
||||
|
||||
## Reading Order
|
||||
|
||||
| Task | Files to Read |
|
||||
| --------------------- | ------------------------- |
|
||||
| Enable bot protection | README → configuration.md |
|
||||
| Workers bot detection | README → api.md |
|
||||
| WAF rule templates | README → patterns.md |
|
||||
| Debug bot issues | gotchas.md |
|
||||
| Advanced analytics | api.md#bot-analytics |
|
||||
|
||||
## Core Concepts
|
||||
|
||||
**Bot Scores**: 1-99 (1 = definitely automated, 99 = definitely human). Threshold: <30 indicates bot traffic. Enterprise gets granular 1-99; Pro/Business get groupings only.
|
||||
|
||||
**Detection Engines**: Heuristics (known fingerprints, assigns score=1), ML (majority of detections, supervised learning on billions of requests), Anomaly Detection (optional, baseline traffic analysis), JavaScript Detections (headless browser detection).
|
||||
|
||||
**Verified Bots**: Allowlisted good bots (search engines, AI crawlers) verified via reverse DNS or Web Bot Auth. Access via `cf.bot_management.verified_bot` or `cf.verified_bot_category`.
|
||||
|
||||
## Platform Limits
|
||||
|
||||
| Plan | Bot Scores | JA3/JA4 | Custom Rules | Analytics Retention |
|
||||
| ------------ | -------------------- | ------- | ------------ | -------------------------- |
|
||||
| Free | No (auto-block only) | No | 5 | N/A (no analytics) |
|
||||
| Pro/Business | Groupings only | No | 20/100 | 30 days (72h at a time) |
|
||||
| Enterprise | 1-99 granular | Yes | 1,000+ | 30 days (1 week at a time) |
|
||||
|
||||
## Basic Patterns
|
||||
|
||||
```typescript
|
||||
// Workers: Check bot score
|
||||
export default {
|
||||
async fetch(request: Request): Promise<Response> {
|
||||
const botScore = request.cf?.botManagement?.score;
|
||||
if (botScore && botScore < 30 && !request.cf?.botManagement?.verifiedBot) {
|
||||
return new Response('Bot detected', { status: 403 });
|
||||
}
|
||||
return fetch(request);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
```txt
|
||||
# WAF: Block definite bots
|
||||
(cf.bot_management.score eq 1 and not cf.bot_management.verified_bot)
|
||||
|
||||
# WAF: Protect sensitive endpoints
|
||||
(cf.bot_management.score lt 50 and http.request.uri.path in {"/login" "/checkout"} and not cf.bot_management.verified_bot)
|
||||
```
|
||||
|
||||
## In This Reference
|
||||
|
||||
- [configuration.md](./configuration.md) - Product tiers, WAF rule setup, JavaScript Detections, ML auto-updates
|
||||
- [api.md](./api.md) - Workers BotManagement interface, WAF fields, JA4 Signals
|
||||
- [patterns.md](./patterns.md) - E-commerce, API protection, mobile app allowlisting, SEO-friendly handling
|
||||
- [gotchas.md](./gotchas.md) - False positives/negatives, score=0 issues, JSD limitations, CSP requirements
|
||||
|
||||
## See Also
|
||||
|
||||
- [waf](../waf/) - WAF custom rules for bot enforcement
|
||||
- [workers](https://developers.cloudflare.com/workers/) - Workers request.cf.botManagement API
|
||||
- [api-shield](../api-shield/) - API-specific bot protection
|
||||
@@ -0,0 +1,175 @@
|
||||
# Bot Management API
|
||||
|
||||
## Workers: BotManagement Interface
|
||||
|
||||
```typescript
|
||||
interface BotManagement {
|
||||
score: number; // 1-99 (Enterprise), 0 if not computed
|
||||
verifiedBot: boolean; // Is verified bot
|
||||
staticResource: boolean; // Serves static resource
|
||||
ja3Hash: string; // JA3 fingerprint (Enterprise, HTTPS only)
|
||||
ja4: string; // JA4 fingerprint (Enterprise, HTTPS only)
|
||||
jsDetection?: {
|
||||
passed: boolean; // Passed JS detection (if enabled)
|
||||
};
|
||||
detectionIds: number[]; // Heuristic detection IDs
|
||||
corporateProxy?: boolean; // From corporate proxy (Enterprise)
|
||||
}
|
||||
|
||||
// DEPRECATED: Use botManagement.score instead
|
||||
// request.cf.clientTrustScore (legacy, duplicate of botManagement.score)
|
||||
|
||||
// Access via request.cf
|
||||
import type { IncomingRequestCfProperties } from '@cloudflare/workers-types';
|
||||
|
||||
export default {
|
||||
async fetch(request: Request): Promise<Response> {
|
||||
const cf = request.cf as IncomingRequestCfProperties | undefined;
|
||||
const botMgmt = cf?.botManagement;
|
||||
|
||||
if (!botMgmt) return fetch(request);
|
||||
if (botMgmt.verifiedBot) return fetch(request); // Allow verified bots
|
||||
if (botMgmt.score === 1) return new Response('Blocked', { status: 403 });
|
||||
if (botMgmt.score < 30) return new Response('Challenge required', { status: 429 });
|
||||
|
||||
return fetch(request);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
## WAF Fields Reference
|
||||
|
||||
```txt
|
||||
# Score fields
|
||||
cf.bot_management.score # 0-99 (0 = not computed)
|
||||
cf.bot_management.verified_bot # boolean
|
||||
cf.bot_management.static_resource # boolean
|
||||
cf.bot_management.ja3_hash # string (Enterprise)
|
||||
cf.bot_management.ja4 # string (Enterprise)
|
||||
cf.bot_management.detection_ids # array
|
||||
cf.bot_management.js_detection.passed # boolean
|
||||
cf.bot_management.corporate_proxy # boolean (Enterprise)
|
||||
cf.verified_bot_category # string
|
||||
|
||||
# Workers equivalent
|
||||
request.cf.botManagement.score
|
||||
request.cf.botManagement.verifiedBot
|
||||
request.cf.botManagement.ja3Hash
|
||||
request.cf.botManagement.ja4
|
||||
request.cf.botManagement.jsDetection.passed
|
||||
request.cf.verifiedBotCategory
|
||||
```
|
||||
|
||||
## JA4 Signals (Enterprise)
|
||||
|
||||
```typescript
|
||||
import type { IncomingRequestCfProperties } from '@cloudflare/workers-types';
|
||||
|
||||
interface JA4Signals {
|
||||
// Ratios (0.0-1.0)
|
||||
heuristic_ratio_1h?: number; // Fraction flagged by heuristics
|
||||
browser_ratio_1h?: number; // Fraction from real browsers
|
||||
cache_ratio_1h?: number; // Fraction hitting cache
|
||||
h2h3_ratio_1h?: number; // Fraction using HTTP/2 or HTTP/3
|
||||
// Ranks (relative position in distribution)
|
||||
uas_rank_1h?: number; // User-Agent diversity rank
|
||||
paths_rank_1h?: number; // Path diversity rank
|
||||
reqs_rank_1h?: number; // Request volume rank
|
||||
ips_rank_1h?: number; // IP diversity rank
|
||||
// Quantiles (0.0-1.0, percentile in distribution)
|
||||
reqs_quantile_1h?: number; // Request volume quantile
|
||||
ips_quantile_1h?: number; // IP count quantile
|
||||
}
|
||||
|
||||
export default {
|
||||
async fetch(request: Request): Promise<Response> {
|
||||
const cf = request.cf as IncomingRequestCfProperties | undefined;
|
||||
const ja4Signals = cf?.ja4Signals as JA4Signals | undefined;
|
||||
|
||||
if (!ja4Signals) return fetch(request); // Not available for HTTP or Worker routing
|
||||
|
||||
// Check for anomalous behavior
|
||||
// High heuristic_ratio or low browser_ratio = suspicious
|
||||
const heuristicRatio = ja4Signals.heuristic_ratio_1h ?? 0;
|
||||
const browserRatio = ja4Signals.browser_ratio_1h ?? 0;
|
||||
|
||||
if (heuristicRatio > 0.5 || browserRatio < 0.3) {
|
||||
return new Response('Suspicious traffic', { status: 403 });
|
||||
}
|
||||
|
||||
return fetch(request);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
## Common Patterns
|
||||
|
||||
See [patterns.md](./patterns.md) for Workers examples: mobile app allowlisting, corporate proxy exemption, datacenter detection, conditional delay, and more.
|
||||
|
||||
## Bot Analytics
|
||||
|
||||
### Access Locations
|
||||
|
||||
- Dashboard: Security > Bots (old) or Security > Analytics > Bot analysis (new)
|
||||
- GraphQL API for programmatic access
|
||||
- Security Events & Security Analytics
|
||||
- Logpush/Logpull
|
||||
|
||||
### Available Data
|
||||
|
||||
- **Enterprise BM**: Bot scores (1-99), bot score source, distribution
|
||||
- **Pro/Business**: Bot groupings (automated, likely automated, likely human)
|
||||
- Top attributes: IPs, paths, user agents, countries
|
||||
- Detection sources: Heuristics, ML, AD, JSD
|
||||
- Verified bot categories
|
||||
|
||||
### Time Ranges
|
||||
|
||||
- **Enterprise BM**: Up to 1 week at a time, 30 days history
|
||||
- **Pro/Business**: Up to 72 hours at a time, 30 days history
|
||||
- Real-time in most cases, adaptive sampling (1-10% depending on volume)
|
||||
|
||||
## Logpush Fields
|
||||
|
||||
```txt
|
||||
BotScore # 1-99 or 0 if not computed
|
||||
BotScoreSrc # Detection engine (ML, Heuristics, etc.)
|
||||
BotTags # Classification tags
|
||||
BotDetectionIDs # Heuristic detection IDs
|
||||
```
|
||||
|
||||
**BotScoreSrc values:**
|
||||
|
||||
- `"Heuristics"` - Known fingerprint
|
||||
- `"Machine Learning"` - ML model
|
||||
- `"Anomaly Detection"` - Baseline anomaly
|
||||
- `"JS Detection"` - JavaScript check
|
||||
- `"Cloudflare Service"` - Zero Trust
|
||||
- `"Not Computed"` - Score = 0
|
||||
|
||||
Access via Logpush (stream to cloud storage/SIEM), Logpull (API to fetch logs), or GraphQL API (query analytics data).
|
||||
|
||||
## Testing with Miniflare
|
||||
|
||||
Miniflare provides mock botManagement data for local development:
|
||||
|
||||
**Default values:**
|
||||
|
||||
- `score: 99` (human)
|
||||
- `verifiedBot: false`
|
||||
- `corporateProxy: false`
|
||||
- `ja3Hash: "25b4882c2bcb50cd6b469ff28c596742"`
|
||||
- `staticResource: false`
|
||||
- `detectionIds: []`
|
||||
|
||||
**Override in tests:**
|
||||
|
||||
```typescript
|
||||
import { getPlatformProxy } from 'wrangler';
|
||||
|
||||
const { cf, dispose } = await getPlatformProxy();
|
||||
// cf.botManagement is frozen mock object
|
||||
expect(cf.botManagement.score).toBe(99);
|
||||
```
|
||||
|
||||
For custom test data, mock request.cf in your test setup.
|
||||
+175
@@ -0,0 +1,175 @@
|
||||
# Bot Management Configuration
|
||||
|
||||
## Product Tiers
|
||||
|
||||
**Note:** Dashboard paths differ between old and new UI:
|
||||
|
||||
- **New:** Security > Settings > Filter "Bot traffic"
|
||||
- **Old:** Security > Bots
|
||||
|
||||
Both UIs access same settings.
|
||||
|
||||
### Bot Score Groupings (Pro/Business)
|
||||
|
||||
Pro/Business users see bot score groupings instead of granular 1-99 scores:
|
||||
|
||||
| Score | Grouping | Meaning |
|
||||
| ----- | ---------------- | ------------------------------ |
|
||||
| 0 | Not computed | Bot Management didn't run |
|
||||
| 1 | Automated | Definite bot (heuristic match) |
|
||||
| 2-29 | Likely automated | Probably bot (ML detection) |
|
||||
| 30-99 | Likely human | Probably human |
|
||||
| N/A | Verified bot | Allowlisted good bot |
|
||||
|
||||
Enterprise plans get granular 1-99 scores for custom thresholds.
|
||||
|
||||
### Bot Fight Mode (Free)
|
||||
|
||||
- Auto-blocks definite bots (score=1), excludes verified bots by default
|
||||
- JavaScript Detections always enabled, no configuration options
|
||||
|
||||
### Super Bot Fight Mode (Pro/Business)
|
||||
|
||||
```txt
|
||||
Dashboard: Security > Bots > Configure
|
||||
- Definitely automated: Block/Challenge
|
||||
- Likely automated: Challenge/Allow
|
||||
- Verified bots: Allow (recommended)
|
||||
- Static resource protection: ON (may block mail clients)
|
||||
- JavaScript Detections: Optional
|
||||
```
|
||||
|
||||
### Bot Management for Enterprise
|
||||
|
||||
```txt
|
||||
Dashboard: Security > Bots > Configure > Auto-updates: ON (recommended)
|
||||
|
||||
# Template 1: Block definite bots
|
||||
(cf.bot_management.score eq 1 and not cf.bot_management.verified_bot and not cf.bot_management.static_resource)
|
||||
Action: Block
|
||||
|
||||
# Template 2: Challenge likely bots
|
||||
(cf.bot_management.score ge 2 and cf.bot_management.score le 29 and not cf.bot_management.verified_bot and not cf.bot_management.static_resource)
|
||||
Action: Managed Challenge
|
||||
```
|
||||
|
||||
## JavaScript Detections Setup
|
||||
|
||||
### Enable via Dashboard
|
||||
|
||||
```txt
|
||||
Security > Bots > Configure Bot Management > JS Detections: ON
|
||||
|
||||
Update CSP: script-src 'self' /cdn-cgi/challenge-platform/;
|
||||
```
|
||||
|
||||
### Manual JS Injection (API)
|
||||
|
||||
```html
|
||||
<script>
|
||||
function jsdOnload() {
|
||||
window.cloudflare.jsd.executeOnce({
|
||||
callback: function (result) {
|
||||
console.log('JSD:', result);
|
||||
}
|
||||
});
|
||||
}
|
||||
</script>
|
||||
<script src="/cdn-cgi/challenge-platform/scripts/jsd/api.js?onload=jsdOnload" async></script>
|
||||
```
|
||||
|
||||
**Use API for**: Selective deployment on specific pages
|
||||
**Don't combine**: Zone-wide toggle + manual injection
|
||||
|
||||
### WAF Rules for JSD
|
||||
|
||||
```txt
|
||||
# NEVER use on first page visit (needs HTML page first)
|
||||
(not cf.bot_management.js_detection.passed and http.request.uri.path eq "/api/user/create" and http.request.method eq "POST" and not cf.bot_management.verified_bot)
|
||||
Action: Managed Challenge (always use Managed Challenge, not Block)
|
||||
```
|
||||
|
||||
### Limitations
|
||||
|
||||
- First request won't have JSD data (needs HTML page first)
|
||||
- Strips ETags from HTML responses
|
||||
- Not supported with CSP via `<meta>` tags
|
||||
- Websocket endpoints not supported
|
||||
- Native mobile apps won't pass
|
||||
- cf_clearance cookie: 15-minute lifespan, max 4096 bytes
|
||||
|
||||
## __cf_bm Cookie
|
||||
|
||||
Cloudflare sets `__cf_bm` cookie to smooth bot scores across user sessions:
|
||||
|
||||
- **Purpose:** Reduces false positives from score volatility
|
||||
- **Scope:** Per-domain, HTTP-only
|
||||
- **Lifespan:** Session duration
|
||||
- **Privacy:** No PII—only session classification
|
||||
- **Automatic:** No configuration required
|
||||
|
||||
Bot scores for repeat visitors consider session history via this cookie.
|
||||
|
||||
## Static Resource Protection
|
||||
|
||||
**File Extensions**: ico, jpg, png, jpeg, gif, css, js, tif, tiff, bmp, pict, webp, svg, svgz, class, jar, txt, csv, doc, docx, xls, xlsx, pdf, ps, pls, ppt, pptx, ttf, otf, woff, woff2, eot, eps, ejs, swf, torrent, midi, mid, m3u8, m4a, mp3, ogg, ts
|
||||
**Plus**: `/.well-known/` path (all files)
|
||||
|
||||
```txt
|
||||
# Exclude static resources from bot rules
|
||||
(cf.bot_management.score lt 30 and not cf.bot_management.static_resource)
|
||||
```
|
||||
|
||||
**WARNING**: May block mail clients fetching static images
|
||||
|
||||
## JA3/JA4 Fingerprinting (Enterprise)
|
||||
|
||||
```txt
|
||||
# Block specific attack fingerprint
|
||||
(cf.bot_management.ja3_hash eq "8b8e3d5e3e8b3d5e")
|
||||
|
||||
# Allow mobile app by fingerprint
|
||||
(cf.bot_management.ja4 eq "your_mobile_app_fingerprint")
|
||||
```
|
||||
|
||||
Only available for HTTPS/TLS traffic. Missing for Worker-routed traffic or HTTP requests.
|
||||
|
||||
## Verified Bot Categories
|
||||
|
||||
```txt
|
||||
# Allow search engines only
|
||||
(cf.verified_bot_category eq "Search Engine Crawler")
|
||||
|
||||
# Block AI crawlers
|
||||
(cf.verified_bot_category eq "AI Crawler")
|
||||
Action: Block
|
||||
|
||||
# Or use dashboard: Security > Settings > Bot Management > Block AI Bots
|
||||
```
|
||||
|
||||
| Category | String Value | Example |
|
||||
| ----------------------- | ---------------------------- | ------------------------------ |
|
||||
| AI Crawler | `AI Crawler` | GPTBot, Claude-Web |
|
||||
| AI Assistant | `AI Assistant` | Perplexity-User, DuckAssistBot |
|
||||
| AI Search | `AI Search` | OAI-SearchBot |
|
||||
| Accessibility | `Accessibility` | Accessible Web Bot |
|
||||
| Academic Research | `Academic Research` | Library of Congress |
|
||||
| Advertising & Marketing | `Advertising & Marketing` | Google Adsbot |
|
||||
| Aggregator | `Aggregator` | Pinterest, Indeed |
|
||||
| Archiver | `Archiver` | Internet Archive, CommonCrawl |
|
||||
| Feed Fetcher | `Feed Fetcher` | RSS/Podcast updaters |
|
||||
| Monitoring & Analytics | `Monitoring & Analytics` | Uptime monitors |
|
||||
| Page Preview | `Page Preview` | Facebook/Slack link preview |
|
||||
| SEO | `Search Engine Optimization` | Google Lighthouse |
|
||||
| Security | `Security` | Vulnerability scanners |
|
||||
| Social Media Marketing | `Social Media Marketing` | Brandwatch |
|
||||
| Webhooks | `Webhooks` | Payment processors |
|
||||
| Other | `Other` | Uncategorized bots |
|
||||
|
||||
## Best Practices
|
||||
|
||||
- **ML Auto-Updates**: Enable on Enterprise for latest models
|
||||
- **Start with Managed Challenge**: Test before blocking
|
||||
- **Always exclude verified bots**: Use `not cf.bot_management.verified_bot`
|
||||
- **Exempt corporate proxies**: For B2B traffic via `cf.bot_management.corporate_proxy`
|
||||
- **Use static resource exception**: Improves performance, reduces overhead
|
||||
@@ -0,0 +1,116 @@
|
||||
# Bot Management Gotchas
|
||||
|
||||
## Common Errors
|
||||
|
||||
### "Bot Score = 0"
|
||||
|
||||
**Cause:** Bot Management didn't run (internal Cloudflare request, Worker routing to zone (Orange-to-Orange), or request handled before BM (Redirect Rules, etc.))
|
||||
**Solution:** Check request flow and ensure Bot Management runs in request lifecycle
|
||||
|
||||
### "JavaScript Detections Not Working"
|
||||
|
||||
**Cause:** `js_detection.passed` always false or undefined due to: CSP headers don't allow `/cdn-cgi/challenge-platform/`, using on first page visit (needs HTML page first), ad blockers or disabled JS, JSD not enabled in dashboard, or using Block action (must use Managed Challenge)
|
||||
**Solution:** Add CSP header `Content-Security-Policy: script-src 'self' /cdn-cgi/challenge-platform/;` and ensure JSD is enabled with Managed Challenge action
|
||||
|
||||
### "False Positives (Legitimate Users Blocked)"
|
||||
|
||||
**Cause:** Bot detection incorrectly flagging legitimate users
|
||||
**Solution:** Check Bot Analytics for affected IPs/paths, identify detection source (ML, Heuristics, etc.), create exception rule like `(cf.bot_management.score lt 30 and http.request.uri.path eq "/problematic-path")` with Action: Skip (Bot Management), or allowlist by IP/ASN/country
|
||||
|
||||
### "False Negatives (Bots Not Caught)"
|
||||
|
||||
**Cause:** Bots bypassing detection
|
||||
**Solution:** Lower score threshold (30 → 50), enable JavaScript Detections, add JA3/JA4 fingerprinting rules, or use rate limiting as fallback
|
||||
|
||||
### "Verified Bot Blocked"
|
||||
|
||||
**Cause:** Search engine bot blocked by WAF Managed Rules (not just Bot Management)
|
||||
**Solution:** Create WAF exception for specific rule ID and verify bot via reverse DNS
|
||||
|
||||
### "Yandex Bot Blocked During IP Update"
|
||||
|
||||
**Cause:** Yandex updates bot IPs; new IPs unrecognized for 48h during propagation
|
||||
**Solution:**
|
||||
|
||||
1. Check Security Events for specific WAF rule ID blocking Yandex
|
||||
2. Create WAF exception:
|
||||
```txt
|
||||
(http.user_agent contains "YandexBot" and ip.src in {<yandex-ip-range>})
|
||||
Action: Skip (WAF Managed Ruleset)
|
||||
```
|
||||
3. Monitor Bot Analytics for 48h
|
||||
4. Remove exception after propagation completes
|
||||
|
||||
Issue resolves automatically after 48h. Contact Cloudflare Support if persists.
|
||||
|
||||
### "JA3/JA4 Missing"
|
||||
|
||||
**Cause:** Non-HTTPS traffic, Worker routing traffic, Orange-to-Orange traffic via Worker, or Bot Management skipped
|
||||
**Solution:** JA3/JA4 only available for HTTPS/TLS traffic; check request routing
|
||||
|
||||
**JA3/JA4 Not User-Unique:** Same browser/library version = same fingerprint
|
||||
|
||||
- Don't use for user identification
|
||||
- Use for client profiling only
|
||||
- Fingerprints change with browser updates
|
||||
|
||||
## Bot Verification Methods
|
||||
|
||||
Cloudflare verifies bots via:
|
||||
|
||||
1. **Reverse DNS (IP validation):** Traditional method—bot IP resolves to expected domain
|
||||
2. **Web Bot Auth:** Modern cryptographic verification—faster propagation
|
||||
|
||||
When `verifiedBot=true`, bot passed at least one method.
|
||||
|
||||
**Inactive verified bots:** IPs removed after 24h of no traffic.
|
||||
|
||||
## Detection Engine Behavior
|
||||
|
||||
| Engine | Score | Timing | Plan | Notes |
|
||||
| --------------------- | ---------- | -------------- | ---------- | ------------------------------- |
|
||||
| Heuristics | Always 1 | Immediate | All | Known fingerprints—overrides ML |
|
||||
| ML | 1-99 | Immediate | All | Majority of detections |
|
||||
| Anomaly Detection | Influences | After baseline | Enterprise | Optional, baseline analysis |
|
||||
| JavaScript Detections | Pass/fail | After JS | Pro+ | Headless browser detection |
|
||||
| Cloudflare Service | N/A | N/A | Enterprise | Zero Trust internal source |
|
||||
|
||||
**Priority:** Heuristics > ML—if heuristic matches, score=1 regardless of ML.
|
||||
|
||||
## Limits
|
||||
|
||||
| Limit | Value | Notes |
|
||||
| ----------------------------- | ----------------------------------------- | ------------------------------------------- |
|
||||
| Bot Score = 0 | Means not computed | Not score = 100 |
|
||||
| First request JSD data | May not be available | JSD data appears on subsequent requests |
|
||||
| Score accuracy | Not 100% guaranteed | False positives/negatives possible |
|
||||
| JSD on first HTML page visit | Not supported | Requires subsequent page load |
|
||||
| JSD requirements | JavaScript-enabled browser | Won't work with JS disabled or ad blockers |
|
||||
| JSD ETag stripping | Strips ETags from HTML responses | May affect caching behavior |
|
||||
| JSD CSP compatibility | Requires specific CSP | Not compatible with some CSP configurations |
|
||||
| JSD meta CSP tags | Not supported | Must use HTTP headers |
|
||||
| JSD WebSocket support | Not supported | WebSocket endpoints won't work with JSD |
|
||||
| JSD mobile app support | Native apps won't pass | Only works in browsers |
|
||||
| JA3/JA4 traffic type | HTTPS/TLS only | Not available for non-HTTPS traffic |
|
||||
| JA3/JA4 Worker routing | Missing for Worker-routed traffic | Check request routing |
|
||||
| JA3/JA4 uniqueness | Not unique per user | Shared by clients with same browser/library |
|
||||
| JA3/JA4 stability | Can change with updates | Browser/library updates affect fingerprints |
|
||||
| WAF custom rules (Free) | 5 | Varies by plan |
|
||||
| WAF custom rules (Pro) | 20 | Varies by plan |
|
||||
| WAF custom rules (Business) | 100 | Varies by plan |
|
||||
| WAF custom rules (Enterprise) | 1,000+ | Varies by plan |
|
||||
| Workers CPU time | Varies by plan | Applies to bot logic |
|
||||
| Bot Analytics sampling | 1-10% adaptive | High-volume zones sampled more aggressively |
|
||||
| Bot Analytics history | 30 days max | Historical data retention limit |
|
||||
| CSP requirements for JSD | Must allow `/cdn-cgi/challenge-platform/` | Required for JSD to function |
|
||||
|
||||
### Plan Restrictions
|
||||
|
||||
| Feature | Free | Pro/Business | Enterprise |
|
||||
| ------------------------- | ------- | ------------ | ---------- |
|
||||
| Granular scores (1-99) | No | No | Yes |
|
||||
| JA3/JA4 | No | No | Yes |
|
||||
| Anomaly Detection | No | No | Yes |
|
||||
| Corporate Proxy detection | No | No | Yes |
|
||||
| Verified bot categories | Limited | Limited | Full |
|
||||
| Custom WAF rules | 5 | 20/100 | 1,000+ |
|
||||
@@ -0,0 +1,181 @@
|
||||
# Bot Management Patterns
|
||||
|
||||
## E-commerce Protection
|
||||
|
||||
```txt
|
||||
# High security for checkout
|
||||
(cf.bot_management.score lt 50 and http.request.uri.path in {"/checkout" "/cart/add"} and not cf.bot_management.verified_bot and not cf.bot_management.corporate_proxy)
|
||||
Action: Managed Challenge
|
||||
```
|
||||
|
||||
## API Protection
|
||||
|
||||
```txt
|
||||
# Protect API with JS detection + score
|
||||
(http.request.uri.path matches "^/api/" and (cf.bot_management.score lt 30 or not cf.bot_management.js_detection.passed) and not cf.bot_management.verified_bot)
|
||||
Action: Block
|
||||
```
|
||||
|
||||
## SEO-Friendly Bot Handling
|
||||
|
||||
```txt
|
||||
# Allow search engine crawlers
|
||||
(cf.bot_management.score lt 30 and not cf.verified_bot_category in {"Search Engine Crawler"})
|
||||
Action: Managed Challenge
|
||||
```
|
||||
|
||||
## Block AI Scrapers
|
||||
|
||||
```txt
|
||||
# Block training crawlers only (allow AI assistants/search)
|
||||
(cf.verified_bot_category eq "AI Crawler")
|
||||
Action: Block
|
||||
|
||||
# Block all AI-related bots (training + assistants + search)
|
||||
(cf.verified_bot_category in {"AI Crawler" "AI Assistant" "AI Search"})
|
||||
Action: Block
|
||||
|
||||
# Allow AI Search, block AI Crawler and AI Assistant
|
||||
(cf.verified_bot_category in {"AI Crawler" "AI Assistant"})
|
||||
Action: Block
|
||||
|
||||
# Or use dashboard: Security > Settings > Bot Management > Block AI Bots
|
||||
```
|
||||
|
||||
## Rate Limiting by Bot Score
|
||||
|
||||
```txt
|
||||
# Stricter limits for suspicious traffic
|
||||
(cf.bot_management.score lt 50)
|
||||
Rate: 10 requests per 10 seconds
|
||||
|
||||
(cf.bot_management.score ge 50)
|
||||
Rate: 100 requests per 10 seconds
|
||||
```
|
||||
|
||||
## Mobile App Allowlisting
|
||||
|
||||
```txt
|
||||
# Identify mobile app by JA3/JA4
|
||||
(cf.bot_management.ja4 in {"fingerprint1" "fingerprint2"})
|
||||
Action: Skip (all remaining rules)
|
||||
```
|
||||
|
||||
## Datacenter Detection
|
||||
|
||||
```typescript
|
||||
import type { IncomingRequestCfProperties } from '@cloudflare/workers-types';
|
||||
|
||||
// Low score + not corporate proxy = likely datacenter bot
|
||||
export default {
|
||||
async fetch(request: Request): Promise<Response> {
|
||||
const cf = request.cf as IncomingRequestCfProperties | undefined;
|
||||
const botMgmt = cf?.botManagement;
|
||||
|
||||
if (botMgmt?.score && botMgmt.score < 30 && !botMgmt.corporateProxy && !botMgmt.verifiedBot) {
|
||||
return new Response('Datacenter traffic blocked', { status: 403 });
|
||||
}
|
||||
|
||||
return fetch(request);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
## Conditional Delay (Tarpit)
|
||||
|
||||
```typescript
|
||||
import type { IncomingRequestCfProperties } from '@cloudflare/workers-types';
|
||||
|
||||
// Add delay proportional to bot suspicion
|
||||
export default {
|
||||
async fetch(request: Request): Promise<Response> {
|
||||
const cf = request.cf as IncomingRequestCfProperties | undefined;
|
||||
const botMgmt = cf?.botManagement;
|
||||
|
||||
if (botMgmt?.score && botMgmt.score < 50 && !botMgmt.verifiedBot) {
|
||||
// Delay: 0-2 seconds for scores 50-0
|
||||
const delayMs = Math.max(0, (50 - botMgmt.score) * 40);
|
||||
await new Promise((r) => setTimeout(r, delayMs));
|
||||
}
|
||||
|
||||
return fetch(request);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
## Layered Defense
|
||||
|
||||
```txt
|
||||
1. Bot Management (score-based)
|
||||
2. JavaScript Detections (for JS-capable clients)
|
||||
3. Rate Limiting (fallback protection)
|
||||
4. WAF Managed Rules (OWASP, etc.)
|
||||
```
|
||||
|
||||
## Progressive Enhancement
|
||||
|
||||
```txt
|
||||
Public content: High threshold (score < 10)
|
||||
Authenticated: Medium threshold (score < 30)
|
||||
Sensitive: Low threshold (score < 50) + JSD
|
||||
```
|
||||
|
||||
## Zero Trust for Bots
|
||||
|
||||
```txt
|
||||
1. Default deny (all scores < 30)
|
||||
2. Allowlist verified bots
|
||||
3. Allowlist mobile apps (JA3/JA4)
|
||||
4. Allowlist corporate proxies
|
||||
5. Allowlist static resources
|
||||
```
|
||||
|
||||
## Workers: Score + JS Detection
|
||||
|
||||
```typescript
|
||||
import type { IncomingRequestCfProperties } from '@cloudflare/workers-types';
|
||||
|
||||
export default {
|
||||
async fetch(request: Request): Promise<Response> {
|
||||
const cf = request.cf as IncomingRequestCfProperties | undefined;
|
||||
const botMgmt = cf?.botManagement;
|
||||
const url = new URL(request.url);
|
||||
|
||||
if (botMgmt?.staticResource) return fetch(request); // Skip static
|
||||
|
||||
// API endpoints: require JS detection + good score
|
||||
if (url.pathname.startsWith('/api/')) {
|
||||
const jsDetectionPassed = botMgmt?.jsDetection?.passed ?? false;
|
||||
const score = botMgmt?.score ?? 100;
|
||||
|
||||
if (!jsDetectionPassed || score < 30) {
|
||||
return new Response('Unauthorized', { status: 401 });
|
||||
}
|
||||
}
|
||||
|
||||
return fetch(request);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
## Rate Limiting by JWT Claim + Bot Score
|
||||
|
||||
```txt
|
||||
# Enterprise: Combine bot score with JWT validation
|
||||
Rate limiting > Custom rules
|
||||
- Field: lookup_json_string(http.request.jwt.claims["{config_id}"][0], "sub")
|
||||
- Matches: user ID claim
|
||||
- Additional condition: cf.bot_management.score lt 50
|
||||
```
|
||||
|
||||
## WAF Integration Points
|
||||
|
||||
- **WAF Custom Rules**: Primary enforcement mechanism
|
||||
- **Rate Limiting Rules**: Bot score as dimension, stricter limits for low scores
|
||||
- **Transform Rules**: Pass score to origin via custom header
|
||||
- **Workers**: Programmatic bot logic, custom scoring algorithms
|
||||
- **Page Rules / Configuration Rules**: Zone-level overrides, path-specific settings
|
||||
|
||||
## See Also
|
||||
|
||||
- [gotchas.md](./gotchas.md) - Common errors, false positives/negatives, limitations
|
||||
@@ -0,0 +1,18 @@
|
||||
# Browser Run (formerly Browser Rendering)
|
||||
|
||||
Use Browser Run for screenshots, PDFs, rendered content extraction, and browser automation. Read the relevant current documentation before implementing; use the [documentation index](https://developers.cloudflare.com/browser-run/llms.txt) to discover additional guides.
|
||||
|
||||
Choose the integration by the work and runtime:
|
||||
|
||||
- For a self-contained screenshot, PDF, or extraction, start with Quick Actions. They are available through REST and Workers bindings; check the chosen action's supported interface.
|
||||
- For multi-step interactions or persistent state, use browser sessions. In Workers, use Cloudflare's Puppeteer or Playwright package; from external scripts or CI, use the CDP integration.
|
||||
- When adapting existing automation, preserve its library where supported and check installed versions against the corresponding guide.
|
||||
|
||||
Read only the reference needed for the task:
|
||||
|
||||
| Task | Reference |
|
||||
| ------------------------------------------------ | ------------------------------------ |
|
||||
| Set up bindings, dependencies, or development | [configuration.md](configuration.md) |
|
||||
| Select an endpoint or browser client API | [api.md](api.md) |
|
||||
| Implement a workflow or manage reusable sessions | [patterns.md](patterns.md) |
|
||||
| Diagnose failures or plan capacity and cost | [gotchas.md](gotchas.md) |
|
||||
@@ -0,0 +1,12 @@
|
||||
# Browser Run APIs
|
||||
|
||||
Read the guide for the chosen interface for request schemas, return types, authentication, and supported options. Keep Quick Actions and browser session APIs distinct when adapting examples.
|
||||
|
||||
| Task | Documentation |
|
||||
| ----------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| Screenshots, PDFs, HTML, scraping, or structured extraction | [Quick Actions](https://developers.cloudflare.com/browser-run/quick-actions/) — links to each action's request options and examples for REST or Workers bindings |
|
||||
| Automate a browser in Workers with Puppeteer | [Puppeteer](https://developers.cloudflare.com/browser-run/puppeteer/) — Cloudflare package, browser operations, and session APIs |
|
||||
| Automate a browser in Workers with Playwright | [Playwright](https://developers.cloudflare.com/browser-run/playwright/) — Cloudflare package, locators, storage state, and tracing |
|
||||
| Control a remote browser from an external runtime | [CDP](https://developers.cloudflare.com/browser-run/cdp/) — session endpoints and links to Puppeteer, Playwright, and other clients |
|
||||
|
||||
The product rename does not imply a rename of API paths or token permissions. Use the identifiers shown in the selected guide.
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
# Browser Run Configuration
|
||||
|
||||
Check the project's runtime, installed client and Wrangler versions, and compatibility date before adapting setup instructions. Cloudflare's packages for Workers and standard clients connecting over CDP have different setup requirements.
|
||||
|
||||
| Task | Documentation |
|
||||
| ---------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| Start a project or configure REST authentication | [Get started](https://developers.cloudflare.com/browser-run/get-started/) — Quick Actions and browser session setup |
|
||||
| Configure a Worker or choose a development mode | [Wrangler reference](https://developers.cloudflare.com/browser-run/reference/wrangler/) — browser bindings, compatibility requirements, and local/remote development |
|
||||
| Install or update a Workers browser client | [Puppeteer](https://developers.cloudflare.com/browser-run/puppeteer/) or [Playwright](https://developers.cloudflare.com/browser-run/playwright/) — package-specific setup and supported versions |
|
||||
| Connect from a script, server, or CI outside Workers | [CDP](https://developers.cloudflare.com/browser-run/cdp/) — authentication and client integration guides |
|
||||
|
||||
Development support depends on the selected interface. Follow its current guidance rather than applying one remote-mode requirement to all Browser Run workflows.
|
||||
@@ -0,0 +1,15 @@
|
||||
# Browser Run Troubleshooting
|
||||
|
||||
Identify the integration and observed failure before changing timeouts or concurrency. A request-rate limit, exhausted browser time, and a closed session require different responses.
|
||||
|
||||
| Concern | Documentation |
|
||||
| ---------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| Quotas, launch rates, concurrency, and session timeouts | [Limits](https://developers.cloudflare.com/browser-run/limits/) — check the current plan and integration-specific limits |
|
||||
| Browser hours and concurrent-browser charges | [Pricing](https://developers.cloudflare.com/browser-run/pricing/) — distinguish Quick Actions from browser sessions |
|
||||
| Missing bindings, action failures, or unsupported behavior | [FAQ](https://developers.cloudflare.com/browser-run/faq/) — diagnose the reported error and runtime constraints |
|
||||
| Puppeteer page evaluation cannot access outer variables | [JavaScript execution](https://pptr.dev/guides/javascript-execution) — browser execution context, passing arguments, and returned values |
|
||||
| Block resources or handle intercepted Puppeteer requests | [Request interception](https://pptr.dev/guides/network-interception) — continue, respond, or abort requests and avoid duplicate handling |
|
||||
| Unexpected disconnects or session loss | [Browser close reasons](https://developers.cloudflare.com/browser-run/reference/browser-close-reasons/) — inspect the recorded close reason before choosing recovery |
|
||||
| Development or compatibility failures | [Wrangler reference](https://developers.cloudflare.com/browser-run/reference/wrangler/) — verify binding configuration and interface-specific development support |
|
||||
|
||||
Before increasing concurrency, check session cleanup and whether the workload can reuse browsers with appropriate isolation; see [patterns.md](patterns.md). Retrieve current limits and pricing when sizing a workload rather than relying on fixed tier tables.
|
||||
@@ -0,0 +1,12 @@
|
||||
# Browser Run Patterns
|
||||
|
||||
Use the current examples for the selected integration instead of translating between Puppeteer, Playwright, and Quick Actions by changing method names.
|
||||
|
||||
| Task | Documentation |
|
||||
| -------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| Implement screenshots, PDFs, or extraction | [Quick Actions](https://developers.cloudflare.com/browser-run/quick-actions/) — choose the action and follow its example |
|
||||
| Build custom interactions | [Puppeteer](https://developers.cloudflare.com/browser-run/puppeteer/) or [Playwright](https://developers.cloudflare.com/browser-run/playwright/) — browser automation examples |
|
||||
| Reconnect across requests | [Reuse sessions](https://developers.cloudflare.com/browser-run/features/reuse-sessions/) — disconnect/reconnect lifecycle and when to use Durable Objects for stateful ownership |
|
||||
| Share browser capacity while isolating users | [Concurrency and session isolation](https://developers.cloudflare.com/browser-run/limits/#how-can-i-manage-concurrency-and-session-isolation-with-browser-run) — tabs, browser contexts, and capacity tradeoffs |
|
||||
|
||||
Quick Actions manage their own session lifecycle. For sessions managed by the application, close pages and browsers on completion or failure. If reuse is intentional, follow the client's disconnect/reconnect semantics and handle expired sessions; closing the browser ends it. Keep cookies and storage isolated between users, and coordinate ownership when several requests can reconnect to the same session.
|
||||
@@ -0,0 +1,112 @@
|
||||
# C3 (create-cloudflare)
|
||||
|
||||
Official CLI for scaffolding Cloudflare Workers and Pages projects with templates, TypeScript, and instant deployment.
|
||||
|
||||
## Quick Start
|
||||
|
||||
```bash
|
||||
# Interactive (recommended for first-time)
|
||||
npm create cloudflare@latest my-app
|
||||
|
||||
# Worker (API/WebSocket/Cron)
|
||||
npm create cloudflare@latest my-api -- --type=hello-world --ts
|
||||
|
||||
# Pages (static/SSG)
|
||||
npm create cloudflare@latest my-site -- --type=web-app --framework=astro --platform=pages
|
||||
```
|
||||
|
||||
## Platform Decision Tree
|
||||
|
||||
```
|
||||
What are you building?
|
||||
|
||||
├─ API / WebSocket / Cron / Email handler
|
||||
│ └─ Workers (default) - no --platform flag needed
|
||||
│ npm create cloudflare@latest my-api -- --type=hello-world
|
||||
|
||||
├─ Static site / SSG / Documentation
|
||||
│ └─ Pages - requires --platform=pages
|
||||
│ npm create cloudflare@latest my-site -- --type=web-app --framework=astro --platform=pages
|
||||
|
||||
├─ Full-stack app (Next.js/Remix/SvelteKit)
|
||||
│ └─ Follow the current framework guide below
|
||||
|
||||
└─ Convert existing project
|
||||
└─ npm create cloudflare@latest . -- --type=pre-existing --existing-script=./src/worker.ts
|
||||
```
|
||||
|
||||
**Critical:** Pages projects require `--platform=pages` flag. Without it, C3 defaults to Workers.
|
||||
|
||||
## Framework Setup
|
||||
|
||||
Fetch the [Workers framework guide](https://developers.cloudflare.com/workers/framework-guides/) for the chosen framework before scaffolding or adapting an existing app. For Next.js, follow [Next.js on Workers](https://developers.cloudflare.com/workers/framework-guides/web-apps/nextjs/); use the [Pages static export guide](https://developers.cloudflare.com/pages/framework-guides/nextjs/deploy-a-static-nextjs-site/) only when targeting a Next.js static export on Pages.
|
||||
|
||||
## Interactive Flow
|
||||
|
||||
When run without flags, C3 prompts in this order:
|
||||
|
||||
1. **Project name** - Directory to create (defaults to current dir with `.`)
|
||||
2. **Application type** - `hello-world`, `web-app`, `demo`, `pre-existing`, `remote-template`
|
||||
3. **Platform** - `workers` (default) or `pages` (for web apps only)
|
||||
4. **Framework** - If web-app: `next`, `remix`, `astro`, `react-router`, `solid`, `svelte`, etc.
|
||||
5. **TypeScript** - `yes` (recommended) or `no`
|
||||
6. **Git** - Initialize repository? `yes` or `no`
|
||||
7. **Deploy** - Deploy now? `yes` or `no` (requires `wrangler login`)
|
||||
|
||||
## Installation Methods
|
||||
|
||||
```bash
|
||||
# NPM
|
||||
npm create cloudflare@latest
|
||||
|
||||
# Yarn
|
||||
yarn create cloudflare
|
||||
|
||||
# PNPM
|
||||
pnpm create cloudflare@latest
|
||||
```
|
||||
|
||||
## In This Reference
|
||||
|
||||
| File | Purpose | Use When |
|
||||
| -------------------- | -------------------------------- | ----------------------------------- |
|
||||
| **api.md** | Complete CLI flag reference | Scripting, CI/CD, advanced usage |
|
||||
| **configuration.md** | Generated files, bindings, types | Understanding output, customization |
|
||||
| **patterns.md** | Workflows, CI/CD, monorepos | Real-world integration |
|
||||
| **gotchas.md** | Troubleshooting failures | Deployment blocked, errors |
|
||||
|
||||
## Reading Order
|
||||
|
||||
| Task | Read |
|
||||
| -------------------------- | ------------------------ |
|
||||
| Create first project | README only |
|
||||
| Set up CI/CD | README → api → patterns |
|
||||
| Debug failed deploy | gotchas |
|
||||
| Understand generated files | configuration |
|
||||
| Full CLI reference | api |
|
||||
| Create custom template | patterns → configuration |
|
||||
| Convert existing project | README → patterns |
|
||||
|
||||
## Post-Creation
|
||||
|
||||
```bash
|
||||
cd my-app
|
||||
|
||||
# Local dev with hot reload
|
||||
npm run dev
|
||||
|
||||
# Generate TypeScript types for bindings
|
||||
npm run cf-typegen
|
||||
|
||||
# Deploy to Cloudflare
|
||||
npm run deploy
|
||||
```
|
||||
|
||||
## See Also
|
||||
|
||||
- **workers/README.md** - Workers runtime, bindings, APIs
|
||||
- **workers-ai/README.md** - AI/ML models
|
||||
- **pages/README.md** - Pages-specific features
|
||||
- **wrangler/README.md** - Wrangler CLI beyond initial setup
|
||||
- **d1/README.md** - SQLite database
|
||||
- **r2/README.md** - Object storage
|
||||
@@ -0,0 +1,70 @@
|
||||
# C3 CLI Reference
|
||||
|
||||
## Invocation
|
||||
|
||||
```bash
|
||||
npm create cloudflare@latest [name] [-- flags] # NPM requires --
|
||||
yarn create cloudflare [name] [flags]
|
||||
pnpm create cloudflare@latest [name] [-- flags]
|
||||
```
|
||||
|
||||
## Core Flags
|
||||
|
||||
| Flag | Values | Description |
|
||||
| ------------------ | --------------------------------------------------------------------------------------------- | ----------------------------------------- |
|
||||
| `--type` | `hello-world`, `web-app`, `demo`, `pre-existing`, `remote-template` | Application type |
|
||||
| `--platform` | `workers` (default), `pages` | Target platform |
|
||||
| `--framework` | `next`, `remix`, `astro`, `react-router`, `solid`, `svelte`, `qwik`, `vue`, `angular`, `hono` | Web framework (requires `--type=web-app`) |
|
||||
| `--lang` | `ts`, `js`, `python` | Language (for `--type=hello-world`) |
|
||||
| `--ts` / `--no-ts` | - | TypeScript for web apps |
|
||||
|
||||
## Deployment Flags
|
||||
|
||||
| Flag | Description |
|
||||
| -------------------------- | ----------------------------------------------------- |
|
||||
| `--deploy` / `--no-deploy` | Deploy immediately (prompts interactive, skips in CI) |
|
||||
| `--git` / `--no-git` | Initialize git (default: yes) |
|
||||
| `--open` | Open browser after deploy |
|
||||
|
||||
## Advanced Flags
|
||||
|
||||
| Flag | Description |
|
||||
| ----------------------------------- | ------------------------------------------------ |
|
||||
| `--template=user/repo` | GitHub template or local path |
|
||||
| `--existing-script=./src/worker.ts` | Existing script (requires `--type=pre-existing`) |
|
||||
| `--category=ai\|database\|realtime` | Demo filter (requires `--type=demo`) |
|
||||
| `--experimental` | Enable experimental features |
|
||||
| `--wrangler-defaults` | Skip wrangler prompts |
|
||||
|
||||
## Environment Variables
|
||||
|
||||
```bash
|
||||
CLOUDFLARE_API_TOKEN=xxx # For deployment
|
||||
CLOUDFLARE_ACCOUNT_ID=xxx # Account ID
|
||||
CF_TELEMETRY_DISABLED=1 # Disable telemetry
|
||||
```
|
||||
|
||||
## Exit Codes
|
||||
|
||||
`0` success, `1` user abort, `2` error
|
||||
|
||||
## Examples
|
||||
|
||||
For framework apps, follow [Framework Setup](README.md#framework-setup).
|
||||
|
||||
```bash
|
||||
# TypeScript Worker
|
||||
npm create cloudflare@latest my-api -- --type=hello-world --lang=ts --no-deploy
|
||||
|
||||
# Astro blog
|
||||
npm create cloudflare@latest my-blog -- --type=web-app --framework=astro --ts --deploy
|
||||
|
||||
# CI: non-interactive
|
||||
npm create cloudflare@latest my-api -- --type=hello-world --lang=ts --no-git --no-deploy
|
||||
|
||||
# GitHub template
|
||||
npm create cloudflare@latest -- --template=cloudflare/templates/worker-openapi
|
||||
|
||||
# Convert existing project
|
||||
npm create cloudflare@latest . -- --type=pre-existing --existing-script=./build/worker.js
|
||||
```
|
||||
@@ -0,0 +1,85 @@
|
||||
# C3 Generated Configuration
|
||||
|
||||
## Output Structure
|
||||
|
||||
```
|
||||
my-app/
|
||||
├── src/index.ts # Worker entry point
|
||||
├── wrangler.jsonc # Cloudflare config
|
||||
├── package.json # Scripts
|
||||
├── tsconfig.json
|
||||
└── .gitignore
|
||||
```
|
||||
|
||||
## wrangler.jsonc
|
||||
|
||||
```jsonc
|
||||
{
|
||||
"$schema": "https://raw.githubusercontent.com/cloudflare/workers-sdk/main/packages/wrangler/config-schema.json",
|
||||
"name": "my-app",
|
||||
"main": "src/index.ts",
|
||||
"compatibility_date": "2026-01-27"
|
||||
}
|
||||
```
|
||||
|
||||
## Binding Placeholders
|
||||
|
||||
C3 generates **placeholder IDs** that must be replaced before deploy:
|
||||
|
||||
```jsonc
|
||||
{
|
||||
"kv_namespaces": [{ "binding": "MY_KV", "id": "placeholder_kv_id" }],
|
||||
"d1_databases": [{ "binding": "DB", "database_id": "00000000-..." }]
|
||||
}
|
||||
```
|
||||
|
||||
**Replace with real IDs:**
|
||||
|
||||
```bash
|
||||
npx wrangler kv namespace create MY_KV # Returns real ID
|
||||
npx wrangler d1 create my-database # Returns real database_id
|
||||
```
|
||||
|
||||
**Deployment error if not replaced:**
|
||||
|
||||
```
|
||||
Error: Invalid KV namespace ID "placeholder_kv_id"
|
||||
```
|
||||
|
||||
## Scripts
|
||||
|
||||
```json
|
||||
{
|
||||
"scripts": {
|
||||
"dev": "wrangler dev",
|
||||
"deploy": "wrangler deploy",
|
||||
"cf-typegen": "wrangler types"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Type Generation
|
||||
|
||||
Run after adding bindings:
|
||||
|
||||
```bash
|
||||
npm run cf-typegen
|
||||
```
|
||||
|
||||
Generates `.wrangler/types/runtime.d.ts`:
|
||||
|
||||
```typescript
|
||||
interface Env {
|
||||
MY_KV: KVNamespace;
|
||||
DB: D1Database;
|
||||
}
|
||||
```
|
||||
|
||||
## Post-Creation Checklist
|
||||
|
||||
1. Review `wrangler.jsonc` - check name, compatibility_date
|
||||
2. Replace placeholder binding IDs with real resource IDs
|
||||
3. Run `npm run cf-typegen`
|
||||
4. Test: `npm run dev`
|
||||
5. Deploy: `npm run deploy`
|
||||
6. Add secrets: `npx wrangler secret put SECRET_NAME`
|
||||
@@ -0,0 +1,97 @@
|
||||
# C3 Troubleshooting
|
||||
|
||||
## Deployment Issues
|
||||
|
||||
### Placeholder IDs
|
||||
|
||||
**Error:** "Invalid namespace ID"
|
||||
**Fix:** Replace placeholders in wrangler.jsonc with real IDs:
|
||||
|
||||
```bash
|
||||
npx wrangler kv namespace create MY_KV # Get real ID
|
||||
```
|
||||
|
||||
### Authentication
|
||||
|
||||
**Error:** "Not authenticated"
|
||||
**Fix:** `npx wrangler login` or set `CLOUDFLARE_API_TOKEN`
|
||||
|
||||
### Name Conflict
|
||||
|
||||
**Error:** "Worker already exists"
|
||||
**Fix:** Change `name` in wrangler.jsonc
|
||||
|
||||
## Platform Selection
|
||||
|
||||
| Need | Platform |
|
||||
| -------------------------------- | ------------------ |
|
||||
| Git integration, branch previews | `--platform=pages` |
|
||||
| Durable Objects, D1, Queues | Workers (default) |
|
||||
|
||||
Wrong platform? Recreate with correct `--platform` flag.
|
||||
|
||||
## TypeScript Issues
|
||||
|
||||
**"Cannot find name 'KVNamespace'"**
|
||||
|
||||
```bash
|
||||
npm run cf-typegen # Regenerate types
|
||||
# Restart TS server in editor
|
||||
```
|
||||
|
||||
**Missing types after config change:** Re-run `npm run cf-typegen`
|
||||
|
||||
## Package Manager
|
||||
|
||||
**Multiple lockfiles causing issues:**
|
||||
|
||||
```bash
|
||||
rm pnpm-lock.yaml # If using npm
|
||||
rm package-lock.json # If using pnpm
|
||||
```
|
||||
|
||||
## CI/CD
|
||||
|
||||
**CI hangs on prompts:**
|
||||
|
||||
```bash
|
||||
npm create cloudflare@latest my-app -- \
|
||||
--type=hello-world --lang=ts --no-git --no-deploy
|
||||
```
|
||||
|
||||
**Auth in CI:**
|
||||
|
||||
```yaml
|
||||
env:
|
||||
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
||||
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
|
||||
```
|
||||
|
||||
## Framework-Specific
|
||||
|
||||
| Framework | Issue | Fix |
|
||||
| --------- | ---------------------- | -------------------------------- |
|
||||
| Next.js | create-next-app failed | `npm cache clean --force`, retry |
|
||||
| Astro | Adapter missing | Install `@astrojs/cloudflare` |
|
||||
| Remix | Module errors | Update `@remix-run/cloudflare*` |
|
||||
|
||||
## Compatibility Date
|
||||
|
||||
**"Feature X requires compatibility_date >= ..."**
|
||||
**Fix:** Update `compatibility_date` in wrangler.jsonc to today's date
|
||||
|
||||
## Node.js Version
|
||||
|
||||
**"Node.js version not supported"**
|
||||
**Fix:** Install Node.js 18+ (`nvm install 20`)
|
||||
|
||||
## Quick Reference
|
||||
|
||||
| Error | Cause | Fix |
|
||||
| ----------------------- | ------------------- | ------------------------------- |
|
||||
| Invalid namespace ID | Placeholder binding | Create resource, update config |
|
||||
| Not authenticated | No login | `npx wrangler login` |
|
||||
| Cannot find KVNamespace | Missing types | `npm run cf-typegen` |
|
||||
| Worker already exists | Name conflict | Change `name` |
|
||||
| CI hangs | Missing flags | Add --type, --lang, --no-deploy |
|
||||
| Template not found | Bad name | Check cloudflare/templates |
|
||||
Loaded 100 of 365 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user