1# Chapter 19: jev-worker — the client on a Cloudflare Worker
2
3`jev-worker` fills in `jev-client`'s two ports for a Cloudflare Worker,
4Rust compiled to WebAssembly (`wasm32-unknown-unknown`) through workers-rs:
5
6- `FetchTransport` posts each request with the Worker's `fetch`.
7- `WorkerRuntime` uses the Worker's clock, its `Delay` timers and
8  `Math.random`.
9
10That is the whole crate, about a hundred lines. The retry policy, the
11response verification and the price are the same code every other host
12runs, in `jev-client` and `jev-protocol`.
13
14## Why a Worker needs its own pair
15
16A Worker is a different sort of place from a server process. There is no
17socket to hold open, so `jev-http`'s kept HTTP/2 connection cannot exist
18here: the runtime's `fetch` opens and pools connections where the Worker
19cannot see them. There is no disk either, so `jev-http`'s on-disk spend
20ledger cannot exist here. What a Worker has is `fetch`, timers, and Durable
21Objects.
22
23So this crate is the transport half only. Two consequences follow from
24`fetch` hiding its connections:
25
26- There is no connection to distrust, so `distrust_connection` does nothing.
27- A send never reports `NotSent`. A failure before any response is
28  `Connect`, which the client retries with backoff, as it would any broken
29  connection.
30
31> **Aside.** A Worker's clock is unusual: `Date.now()` and
32> `performance.now()` only move forward across I/O, never during CPU work.
33> That is still right for the client, because every interval it measures
34> spans a `fetch` or a timer.
35
36```mermaid
37sequenceDiagram
38  participant W as Your Worker
39  participant C as jev_client::Client
40  participant F as FetchTransport
41  participant J as api.typesafe.ai
42  W->>C: ask(state, questions)
43  C->>F: send(HttpRequest)
44  F->>J: fetch POST /v1/systemone
45  J-->>F: status, headers, body
46  F-->>C: HttpResponse (body capped at 8 MiB)
47  C-->>W: Answered, or ClientError after retries
48```
49
50## Try it
51
52```
53cargo build -p jev-worker --target wasm32-unknown-unknown
54```
55
56That needs the `wasm32-unknown-unknown` target installed for your Rust
57toolchain. The crate also compiles in the native workspace build, so
58`cargo test --workspace` checks it, but nothing there can call it.
59
60Asking from a Worker:
61
62```rust
63use jev_worker::{FetchTransport, WorkerRuntime};
64
65let client = jev_client::Client::new(FetchTransport::api(), WorkerRuntime, model, &key)?;
66let answered = client.ask(&state, &questions).await?;
67```
68
69`FetchTransport::api()` posts to `jev_protocol::ENDPOINT`;
70`FetchTransport::new(url)` posts somewhere else, such as a relay or a test
71server. lmjtfy (`~/lmjtfy`) is the Worker that uses it, with its spend
72guard in a Durable Object.
73
74## For the people who maintain it
75
76### In this folder
77
78| Path | What |
79| --- | --- |
80| [src/](src/) | The code: Chapter 19½. |
81| [Cargo.toml](Cargo.toml) | Dependencies: `bytes`, `futures-util`, `http`, `jev-client`, `jev-protocol`, `js-sys`, `worker` (0.8). |
82| [CLAUDE.md](CLAUDE.md) | Invariants for agents. |
83
84The workspace pins `worker` only to its API (`0.8`). The consumer pins
85`wasm-bindgen` to the exact version of the CLI its build uses.
86
87← Previous: [Chapter 18¾: jev-client/src/client](../jev-client/src/client/) · Up: [jevcrates](../) · Next: [Chapter 19½: inside jev-worker/src](src/) →