Chapter 19: jev-worker — the client on a Cloudflare Worker
jev-worker fills in jev-client's two ports for a Cloudflare Worker,
Rust compiled to WebAssembly (wasm32-unknown-unknown) through workers-rs:
FetchTransportposts each request with the Worker'sfetch.WorkerRuntimeuses the Worker's clock, itsDelaytimers andMath.random.
That is the whole crate, about a hundred lines. The retry policy, the
response verification and the price are the same code every other host
runs, in jev-client and jev-protocol.
Why a Worker needs its own pair
A Worker is a different sort of place from a server process. There is no
socket to hold open, so jev-http's kept HTTP/2 connection cannot exist
here: the runtime's fetch opens and pools connections where the Worker
cannot see them. There is no disk either, so jev-http's on-disk spend
ledger cannot exist here. What a Worker has is fetch, timers, and Durable
Objects.
So this crate is the transport half only. Two consequences follow from
fetch hiding its connections:
- There is no connection to distrust, so
distrust_connectiondoes nothing. - A send never reports
NotSent. A failure before any response isConnect, which the client retries with backoff, as it would any broken connection.
Aside. A Worker's clock is unusual:
Date.now()andperformance.now()only move forward across I/O, never during CPU work. That is still right for the client, because every interval it measures spans afetchor a timer.
sequenceDiagram participant W as Your Worker participant C as jev_client::Client participant F as FetchTransport participant J as api.typesafe.ai W->>C: ask(state, questions) C->>F: send(HttpRequest) F->>J: fetch POST /v1/systemone J-->>F: status, headers, body F-->>C: HttpResponse (body capped at 8 MiB) C-->>W: Answered, or ClientError after retries
Try it
cargo build -p jev-worker --target wasm32-unknown-unknown
That needs the wasm32-unknown-unknown target installed for your Rust
toolchain. The crate also compiles in the native workspace build, so
cargo test --workspace checks it, but nothing there can call it.
Asking from a Worker:
use jev_worker::{FetchTransport, WorkerRuntime};
let client = jev_client::Client::new(FetchTransport::api(), WorkerRuntime, model, &key)?;
let answered = client.ask(&state, &questions).await?;
FetchTransport::api() posts to jev_protocol::ENDPOINT;
FetchTransport::new(url) posts somewhere else, such as a relay or a test
server. lmjtfy (~/lmjtfy) is the Worker that uses it, with its spend
guard in a Durable Object.
For the people who maintain it
In this folder
| Path | What |
|---|---|
| src/ | The code: Chapter 19½. |
| Cargo.toml | Dependencies: bytes, futures-util, http, jev-client, jev-protocol, js-sys, worker (0.8). |
| CLAUDE.md | Invariants for agents. |
The workspace pins worker only to its API (0.8). The consumer pins
wasm-bindgen to the exact version of the CLI its build uses.
← Previous: Chapter 18¾: jev-client/src/client · Up: jevcrates · Next: Chapter 19½: inside jev-worker/src →