Cloudflare Workers
Run Checkrd at the edge with policy enforcement in workerd at sub-millisecond overhead.
Cloudflare Workers
Checkrd is built to run at the edge. The WASM core is wasm32-wasip1, a clean format Cloudflare Workers' runtime (workerd) loads natively via WebAssembly.compile. No node:* shims, no Node polyfills.
Install
npm install checkrdQuickstart
import OpenAI from "openai";
import { withCheckrd } from "checkrd/cloudflare";
interface Env {
CHECKRD_API_KEY: string;
OPENAI_API_KEY: string;
}
export default {
fetch: withCheckrd<Env>(
async (request, env, _ctx, fetch) => {
// `fetch` is Checkrd-enforced. Plug it into any vendor SDK
// that accepts a `fetch` option.
const openai = new OpenAI({ fetch, apiKey: env.OPENAI_API_KEY });
const out = await openai.chat.completions.create({
model: "gpt-4o-mini",
messages: [{ role: "user", content: await request.text() }],
});
return Response.json(out);
},
(env) => ({
apiKey: env.CHECKRD_API_KEY,
agentId: "01234567-89ab-cdef-0123-456789abcdef",
}),
),
};withCheckrd(handler, optionsFn) is a HOC that:
- Initializes the WASM engine on first request and caches it across the worker's lifetime (Workers reuse isolates).
- Resolves options per-request from the bound
env. The second argument is a(env) => optionsfactory, NOT a plain object. This lets secrets flow from Cloudflare bindings withoutprocess.env. - Passes a Checkrd-enforced
fetchas the handler's fourth argument; pipe it into OpenAI / Anthropic / Vercel AI SDK clients.
With the Vercel AI SDK
import { withCheckrd } from "checkrd/cloudflare";
import { checkrdMiddleware } from "checkrd/ai-sdk";
import { getEngine, getSink } from "checkrd";
import { generateText, wrapLanguageModel } from "ai";
import { openai } from "@ai-sdk/openai";
export default {
fetch: withCheckrd<Env>(
async (request, env, _ctx, _fetch) => {
const model = wrapLanguageModel({
model: openai("gpt-4o"),
middleware: checkrdMiddleware({
engine: getEngine(),
enforce: true,
agentId: "01234567-89ab-cdef-0123-456789abcdef",
sink: getSink(),
}),
});
const { text } = await generateText({
model,
prompt: await request.text(),
});
return new Response(text);
},
(env) => ({ apiKey: env.CHECKRD_API_KEY, agentId: "01234567-89ab-cdef-0123-456789abcdef" }),
),
};Loading the policy
Don't pass policy:. With an apiKey configured (as in every example above), the SDK fetches your agent's currently-published DSSE-signed bundle from GET /v1/agents/:id/control/state on the first request and installs it before your handler runs. The signed-bundle SSE channel streams updates to the long-lived isolate in milliseconds — no redeploy required.
Workers have no filesystem, so the dashboard is the only place a Workers operator should edit policy. The pattern doubles as a way to push hotfixes without re-publishing to Cloudflare.
Telemetry
The default sink in Workers buffers events in memory and flushes via ctx.waitUntil(...) at the end of each request. No daemon thread, no polling; fits the workerd lifecycle.
(env) => ({
apiKey: env.CHECKRD_API_KEY,
agentId: "01234567-89ab-cdef-0123-456789abcdef",
// sink is auto-configured against env.CHECKRD_API_KEY; override only if needed.
})Caveats
- No
node:*imports. Don't passpolicy: fs.readFileSync(...); that path is Node-only. Pass the policy as a string inpolicy:after fetching it from R2, KV, or a bundled import. - Cold start. First request loads the WASM (~10ms cold). Subsequent requests reuse the compiled module across the isolate's lifetime. For sub-ms p99, keep your worker hot via Cloudflare's Smart Placement.
- Cloudflare-specific globals. The Cloudflare adapter uses
crypto.subtlefor SHA-256 (the WASM integrity check) andWebAssembly.compilefor module loading; both are workerd built-ins. No polyfill required.