Runnable Flue 2.0 agents and channels for Cloudflare. Each E2E script builds a Worker, deploys it, tests the live endpoint, and deletes the Worker. The tests do not use mocks.
Each snippet is a self-contained Cloudflare app. It has its own package file, Wrangler configuration, and source directory.
Demo site: flue.coey.dev, a guided tour of Flue on Cloudflare, auto-generated from this repo.
examples/ one CF product per folder, the smallest Flue snippet that uses it
recipes/ compositions, Flue + multiple primitives + receipts/proofs
templates/ forkable starters, production-shape, fork-and-ship
Every deployed snippet is public on *.workers.dev and binds Workers AI,
so each agent route requires the per-run SNIPPET_API_KEY sent as an
x-api-key header. Each run-e2e.sh generates a fresh one and injects it with
wrangler deploy --var. Without it the route answers 401.
templates/github-app mounts no agent route at all: its only inbound surface
is the HMAC-verified GitHub webhook.
Every snippet is a Flue agent: prompt it at POST /agents/<name>/<id>,
which is answered 202, then GET the same URL to read the settled
conversation. Verified provider webhook ingress uses channels
(POST /channels/<name>/<suffix>). Flue 2 removed workflows; work that was a
workflow step is now a tool the agent calls.
git clone https://github.com/acoyfellow/flue-snippets
cd flue-snippets
# https://dash.cloudflare.com/profile/api-tokens, Workers Scripts:Edit + Workers AI:Read
# Put them in .env (each run-e2e.sh sources it) or export them:
export CLOUDFLARE_API_TOKEN=...
export CLOUDFLARE_ACCOUNT_ID=...
bun ex:workers-ai # from the repo root
# or: cd examples/workers-ai && bash run-e2e.sh~60 seconds: install (first run), build, deploy, assert, delete. ~$0.0001 in
Workers AI usage. Each snippet is self-contained (its own package.json +
src/ + wrangler.jsonc) and installs its own deps on first run.
The agent it just ran:
// examples/workers-ai/src/agents/workers-ai.ts
'use agent';
import { type AgentProps, useModel } from '@flue/runtime';
export function WorkersAi(_props: AgentProps) {
useModel('cloudflare/@cf/moonshotai/kimi-k2.6');
return 'Answer each user request directly and concisely.';
}The 'use agent' directive registers the exported function as an agent at build
time. Prompt it with POST /agents/workers-ai/<conversationId> and a body of
{ "kind": "user", "body": "..." }, then GET the same URL for the reply.
One Flue snippet, one Cloudflare binding.
| Example | Cloudflare product |
|---|---|
| workers-ai | Workers AI |
| effect-hello | Workers AI + Effect v4 (small Flue agent with an Effect program) |
| kv | Workers KV |
| r2 | R2 |
| d1 | D1 |
| durable-objects | Durable Objects (agent: same id → same DO, isolated per instance) |
| ai-gateway | AI Gateway |
| queues | Queues |
| vectorize | Vectorize |
| browser-rendering | Browser Rendering |
| worker-loader | Dynamic Workers |
| hyperdrive | Hyperdrive |
| email-workers | Email Service |
Flue compositions with Cloudflare primitives and reviewable receipt, evaluation, or observability
layers (including @acoyfellow/lab,
gateproof, and Braintrust).
| Recipe | Composes |
|---|---|
| lab-receipt | Workers AI + lab |
| do-session | Durable Objects (agent, durable session memory) |
| do-governor | Durable Objects (workflow, repetition governor) |
| lab-checkpoint | Durable Objects + lab |
| ai-gateway | AI Gateway + Workers AI |
| gateway-lab | AI Gateway + Workers AI + lab |
| github-triage | Workers AI + structured output (valibot schema) |
| chat-thinking | Flue + Cloudflare Think (co-hosted DO chat agent) |
| virtual-sandbox | Flue virtual sandbox (just-bash) + R2 |
| mcp-client | Flue + co-hosted MCP server (Workers) |
| dynamic-workflow | Durable Objects + Workflows |
| event-trigger | Workers AI + structured output (one signed-webhook front door for Sentry / PagerDuty / GitLab CI / cron) |
| braintrust-trace | Braintrust application tracing + Workers AI |
| braintrust-ai-gateway | Braintrust AI Gateway + gateway logging |
| braintrust-eval | Braintrust Evals + deployed Workers AI endpoint |
| braintrust-otel | OpenTelemetry + Braintrust span exporter + Workers AI |
Each recipe's README explains what it composes, what it proves, and how to run it. The Braintrust
recipes require a BRAINTRUST_API_KEY (injected into the ephemeral Worker via
wrangler deploy --var); they run explicitly with bun rx:braintrust-* rather than in the CI
matrix. No ALCHEMY_PASSWORD is used. Deploys use wrangler, not alchemy.
bun ex:<name> # examples/<name>/run-e2e.sh
bun rx:<name> # recipes/<name>/run-e2e.sh
bun tpl:<name> # templates/<name>/run-e2e.shSee package.json for the full list.
bun run updateThat repeatable maintenance command runs bun update in the repo and in
site/, refreshes the homepage's visible “Last dependency refresh”
timestamp when Bun bumps package manifest versions, then runs the site production
build. If the Flue CLI/runtime or a dependency used by a snippet changes shape,
also update the affected example/recipe/template and run its live E2E (bun ex:<name>, bun rx:<name>, or bun tpl:<name>) before shipping.
Every run-e2e.sh does the same five things:
vite build, compiles the project (agents/channels undersrc/) intodist/<name>/with a Flue-generatedwrangler.json. The'use agent'scanner registers every exported agent at build time.wrangler deploy --config dist/<name>/wrangler.json, deploys the Worker, creating any per-run resource (KV/R2/D1/Queue/Vectorize) first and injecting secrets/vars with--var. Prints the URL.- Warmup, POSTs the agent route (
/agents/<name>/<id>) with retries until it answers202. The retry is mandatory: a freshly deployed Worker can 500 or refuse admission for ~10s while the route propagates and Workers AI warms up. Flue has no/healthroute. - Assert,
GETthe same conversation URL and read the settled snapshot (messages[].parts[], where the reply is the part withtype: 'text'); recipes run agateproof.plan.tswith aprobe.ts(purefetch+ JSON). wrangler delete(and delete any created resource), then verify zero leftover Workers. Trapped onEXIT INT TERM.
Flue owns the wrangler config: the Vite plugin merges its generated bindings +
DO migrations into dist/<name>/wrangler.json, and wrangler deploy ships it. Each
snippet is self-contained. It has its own package.json, wrangler.jsonc, and src/.
.github/workflows/e2e.yml is workflow_dispatch-only. From the Actions tab, pick all, examples, recipes, templates, or a single target. max-parallel: 1 because Workers AI rate-limits hard under parallel load on a personal account.
Secrets:
CF_API_TOKEN_E2E, Workers Scripts:Edit + Workers AI:Read, plus permissions for any product-specific targets you enable (R2 / D1 / KV / Queues / AI Gateway / Vectorize / Browser Rendering / Worker Loader / Hyperdrive / Email).CF_ACCOUNT_ID_E2E, Cloudflare account ID.EMAIL_FROM,EMAIL_TO, only needed if you enableexamples/email-workers. Without them, the send call returns a structured error and the assertion still passes (it accepts either a real send or a structuredE_*code).
The Braintrust recipes currently run explicitly with bun rx:braintrust-* rather than in this
matrix. Enrolling them requires a BRAINTRUST_API_KEY (injected into the ephemeral Worker via
wrangler deploy --var). No ALCHEMY_PASSWORD is needed. Deploys use wrangler, not alchemy.
Does it deploy a Worker? Yes. Each run-e2e.sh runs vite build then wrangler deploy, hits a real *.workers.dev URL, then wrangler deletes it. CI does the same. The tests do not use mocks.
What does it cost? ~$0.0001 per snippet per run (one Workers AI @cf/moonshotai/kimi-k2.6 call). Check your Cloudflare plan and limits before you run the full matrix.
Do all snippets run on a stock token? Most do. A few need extra access and will otherwise report a clear, non-fatal error: hyperdrive (token needs Hyperdrive), braintrust-* (need BRAINTRUST_API_KEY; braintrust-ai-gateway also needs a model provider configured in your Braintrust AI Gateway), email-workers (a verified sender, else it returns a structured E_* code and still passes).
How does it deploy? Flue 2 owns the deploy artifact through Vite: vite build emits dist/<name>/ with a generated wrangler.json (merged bindings + DO migrations), and wrangler deploy --config dist/<name>/wrangler.json ships it. Agents live in src/agents/, routing in src/app.ts, and verified provider ingress in src/channels/.
Why does CI run sequentially? Workers AI rate-limits aggressively on personal accounts under parallel load. max-parallel: 1 keeps the matrix green.
Can I run only one? Yes, every example, recipe, and template is independent. bun ex:<name>, bun rx:<name>, or bun tpl:<name>. Or trigger a single target from the Actions dropdown.
See CONTRIBUTING.md. Security issues: SECURITY.md.
MIT.