Crux
CookbookRuntime Recipes

Cloudflare Runtime Mapping

Recipe-only code sketch for mapping Cloudflare Queues, Durable Objects, D1, and alarms to Runtime Engine concepts.

Cloudflare is a recipe in v1, not a bundled first-party Crux runtime adapter. The examples below show the shape an adapter author can build: Crux still owns target identity, payload JSON, idempotency, and wake handling; Cloudflare provides queue delivery and storage primitives.

Runtime config sketch

Use genericQueue() when your Cloudflare Queue worker forwards wake messages to a deployed Crux runtime endpoint. The store below is a placeholder for a future Cloudflare/D1 or Durable Object store adapter.

import { config } from '@use-crux/core'
import { genericQueue, serverless } from '@use-crux/core/runtime'
import { cloudflareStore } from './cloudflare-runtime-store'

export default config({
  runtime: serverless({
    store: cloudflareStore({
      d1Binding: 'CRUX_RUNTIME_DB',
      namespace: process.env.CRUX_RUNTIME_NAMESPACE,
    }),
    wake: genericQueue({
      secret: process.env.CRUX_RUNTIME_WAKE_SECRET!,
      maxDelayMs: 15 * 60 * 1000,
      enqueue: async (message) => {
        await env.CRUX_WAKE_QUEUE.send(message, {
          delaySeconds: Math.min(message.envelope.notBeforeDelayMs ?? 0, 900) / 1000,
        })
      },
    }),
    publicUrl: process.env.CRUX_PUBLIC_URL,
  }),
})

Queue consumer

The worker receives Crux wake messages from Cloudflare Queues and forwards the signed body and headers exactly as produced by genericQueue().

export default {
  async queue(batch: MessageBatch<RuntimeWakeMessage>, env: Env) {
    for (const message of batch.messages) {
      const response = await fetch(message.body.url, {
        method: 'POST',
        headers: message.body.headers,
        body: message.body.body,
      })

      if (response.status >= 500 || response.status === 409) {
        message.retry()
        continue
      }

      message.ack()
    }
  },
}

interface RuntimeWakeMessage {
  id: string
  url: string
  body: string
  headers: Record<string, string>
  envelope: {
    idempotencyKey: string
    notBeforeDelayMs?: number
  }
}

Store boundary sketch

A real adapter would implement RuntimeStoreAdapter against D1 tables or Durable Object state, then run the shared conformance suite.

import type { RuntimeStoreAdapter } from '@use-crux/core/runtime'

export function cloudflareStore(options: {
  d1Binding: string
  namespace?: string
}): RuntimeStoreAdapter {
  return {
    id: 'cloudflare-d1',
    capabilities: {
      transactions: 'serializable',
    },
    async transact(fn) {
      // Use D1 transactions or Durable Object serialization here.
      // The callback receives state/events/waiters/timers/outbox/leases ports.
      return d1Transaction(options.d1Binding, fn)
    },
    setup: {
      check: async () => checkD1RuntimeSchema(options.d1Binding),
      apply: async () => applyD1RuntimeSchema(options.d1Binding),
    },
  }
}

Mapping notes

Runtime concernCloudflare substrate
Wake/task deliveryCloudflare Queues
State/events/leasesDurable Objects or D1
TimersDurable Object alarms, Queue delays, or store-backed scans
Live updatesDurable Object WebSockets or SSE

Queue delivery is at least once. Keep Crux idempotency keys as the durable identity and acknowledge only after the runtime endpoint accepts the wake.

On this page