checkrd

Cloudflare Workers

Run Checkrd at the edge — policy enforcement in workerd with 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

bash
npm install checkrd

Quickstart

typescript
import { withCheckrd } from "checkrd/integrations/cloudflare";

export default withCheckrd<Env>(
  async (request, env, ctx, checkrd) => {
    // Your worker logic. `checkrd` is the initialized engine + sink.
    return new Response("ok");
  },
  {
    apiKey: (env) => env.CHECKRD_API_KEY, // pulled from Cloudflare secrets
    agentId: "edge-worker",
    policyUrl: (env) => env.CHECKRD_POLICY_URL, // optional — fetch policy from R2 or KV
  },
);

withCheckrd is a HOC that:

  1. Initializes the WASM engine on first request and caches it across the worker's lifetime (Workers reuse isolates).
  2. Loads the policy from a URL (R2, KV, or external) — works around Workers having no filesystem.
  3. Passes the live checkrd context to your handler so you can wrap outbound clients or attach the AI SDK middleware.

With the Vercel AI SDK

typescript
import { withCheckrd } from "checkrd/integrations/cloudflare";
import { checkrdMiddleware } from "checkrd/integrations/ai-sdk";
import { generateText, wrapLanguageModel } from "ai";
import { openai } from "@ai-sdk/openai";

export default withCheckrd<Env>(
  async (request, env, ctx, checkrd) => {
    const model = wrapLanguageModel({
      model: openai("gpt-4o"),
      middleware: checkrdMiddleware({
        engine: checkrd.engine,
        enforce: true,
        agentId: "edge-worker",
        sink: checkrd.sink,
      }),
    });

    const { text } = await generateText({
      model,
      prompt: await request.text(),
    });

    return new Response(text);
  },
  { apiKey: (env) => env.CHECKRD_API_KEY, agentId: "edge-worker" },
);

Loading the policy

Workers have no filesystem. Three options:

  1. Embed at build time — pass a YAML string in policy:.
  2. Cloudflare R2 — store policy.yaml in an R2 bucket; pass policyUrl: env => env.POLICY_URL.
  3. Cloudflare KV — store the policy in KV; load it in the handler before calling Checkrd's reload API.

For policies that change frequently, prefer Checkrd's signed-bundle SSE (controlPlaneUrl: "https://api.checkrd.io") — the engine receives DSSE-verified bundles over a long-lived stream and reloads in place without a worker restart.

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.

typescript
{
  apiKey: (env) => env.CHECKRD_API_KEY,
  agentId: "edge-worker",
  // sink is auto-configured against env.CHECKRD_API_KEY; override only if needed.
}

Caveats

  • No node:* imports. Don't pass policy: fs.readFileSync(...) — that path is Node-only. Use policy: "..." (string), policyUrl, or policyBytes (Uint8Array).
  • 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.subtle for SHA-256 (the WASM integrity check) and WebAssembly.compile for module loading — both are workerd built-ins. No polyfill required.