Skip to main content
Target id: wasm-cloudflare-workers Family: Wasm host Stage: Research — target profile and capability map accepted into the registry; no local backend yet. Related: Wasm family, Capability registry, Shared scenario, RFC 0002.

Summary

Cloudflare Workers is a non-blockchain Wasm-host target. It lets the same portable business core that compiles to blockchain Wasm targets (NEAR, CosmWasm) also run as an off-chain edge worker. The goal is to share verified Lean business logic between on-chain contracts and chain-side services such as oracles, keepers, indexers, simulators, and hybrid dApp backends. Because Workers is not a blockchain, many chain-specific capabilities are reinterpreted or unsupported. The portable core (pure logic, state transitions, types, proofs) stays identical; only the capability adapter changes.

Pipeline

Lean contract
  -> Lean frontend / LCNF
  -> EmitZig
  -> generated Zig contract module
  -> Cloudflare Workers Zig runtime + host bridge
  -> wasm32-freestanding or wasm32-wasi Wasm (WASI imports stripped/stubbed)
  -> Wrangler package (.wasm + wrangler.toml + metadata)
  -> wrangler dev / wrangler deploy
  -> Workers smoke (Miniflare or remote)
The host bridge exposes Workers platform APIs to Lean @[extern "lean_cf_*"] declarations:
  • KV read/write for persistent scalar/map storage.
  • Request metadata / Date.now() for environment reads.
  • fetch() for cross-call invoke.
  • console.log for events.

Target Profile

def wasmCloudflareWorkers : TargetProfile := {
  id := "wasm-cloudflare-workers",
  family := .wasmHost,
  artifactKind := .wasm,
  capabilities := #[
    .storageScalar,
    .storageMap,
    .callerSender,
    .eventsEmit,
    .crosscallInvoke,
    .envBlock,
    .cryptoHash,
    .controlConditional,
    .controlBoundedLoop,
    .dataFixedArray,
    .dataStruct,
    .assertions
  ],
  requiredTools := #["zig", "wrangler"]
}

Capability Mapping

Capability idCloudflare Workers mappingNotes
storage.scalarWorkers KV get/put or Durable Object state fieldKV is eventually consistent; DO gives strong consistency per object
storage.mapKV with key prefix, or DO in-memory MapFirst implementation can use string-keyed KV
caller.senderRequest header, JWT claim, or env bindingConfigurable per deployment; no built-in signer concept
events.emitconsole.log with structured JSONAlso routable to Workers Logs / Tail Workers
crosscall.invokefetch() to another Worker, service binding, or external HTTP endpointReturns HTTP response, not blockchain call result
env.blockDate.now()No block height; only timestamp
crypto.hashWeb Crypto crypto.subtle.digest or Zig implementationSHA-256 first; keccak256 via library if needed
control.conditionalLean/Zig loweringNative support
control.bounded_loopLean/Zig loweringNative support
data.fixed_arrayLean/Zig value typeNative support
data.structLean/Zig value typeNative support
assertions.checkRuntime panic / error responseReturns HTTP 500 or structured error
Not supported by design:
  • value.native — Workers has no native token transfer semantics.
  • storage.pda, crosscall.cpi — Solana-specific.
  • zk.circuit, zk.proof — not a ZK target.

Runtime Profile

Strategy B from RFC 0003: full Lean runtime plus a Cloudflare-specific host bridge. Constraints:
  • Single-threaded; no POSIX/libuv assumptions.
  • No filesystem; all I/O through host bridge imports.
  • Wasm-safe allocator (bump or custom).
  • No native GMP; use Zig bigint or restrict arithmetic.
  • HTTP request/response is the entry boundary, not a blockchain transaction.

Entrypoint Shape

Exported Wasm function:
fetch(request_ptr, request_len, env_ptr, ctx_ptr) -> response_ptr
The bridge:
  1. Deserializes the incoming request (method, path, headers, body).
  2. Dispatches to a contract entrypoint based on path or JSON message.
  3. Runs the portable core with capabilities bound to Workers APIs.
  4. Serializes the result into an HTTP response.
Example HTTP mapping for Counter:
  • POST /initializeinitialize
  • POST /incrementincrement
  • GET /countget

Storage Backend Options

Option A: Workers KV (default for v0)

  • Pros: simple, global, cheap.
  • Cons: eventual consistency, no transactions, no numeric increment atomicity guarantees across simultaneous edge invocations.

Option B: Durable Objects

  • Pros: strong consistency, single-object atomicity, in-memory state.
  • Cons: requires object ID routing, more complex binding setup.
v0 should use KV for Counter; v1 should introduce DO as an optional storage.scalar.strong capability.

Build Commands (planned)

proof-forge build --target wasm-cloudflare-workers \
  --out build/cloudflare-workers \
  Examples/CloudflareWorkers/Counter.lean
Smoke:
wrangler dev
# or
npx miniflare build/cloudflare-workers

Example Location

TargetPathStatus
Cloudflare WorkersExamples/CloudflareWorkers/Counter.leanPlanned, not in repo

Open Questions

  1. Should the host bridge be written in Zig (consistent with NEAR/CosmWasm) or in Rust (better Cloudflare ecosystem)?
  2. Should KV or DO be the default storage backend for the Counter spike?
  3. How should authentication/caller identity be configured in wrangler.toml?
  4. Should crosscall.invoke support service bindings only, or any HTTP URL?
  5. How do we test atomicity and consistency in the shared scenario when Workers is not transaction-based?

Acceptance Criteria for Spike Exit

  • wasm-cloudflare-workers target profile is in the registry.
  • Counter example compiles to a Workers-compatible .wasm.
  • wrangler dev serves POST /increment and GET /count correctly.
  • Artifact metadata records target: wasm-cloudflare-workers and capabilities used.