Six parts in, I have to admit something: I do not care what the millionth digit of pi is. Nobody does. Pi was a stand-in from day one, a workload chosen because it is easy to explain, easy to verify, and embarrassing in exactly the right way, meaning embarrassingly parallel.
The actual bet was always this: if the coordinator is built right, pi should be replaceable. The assignment logic, the R=2 byte-equality verification from the last part, the canaries, the trust scores, the healing, none of that should care what the nodes are computing. This part tests that bet: extract the workload-agnostic seam, push a second, genuinely different workload through the unmodified core, then be honest about which workloads this grid can ever serve and which it never will.
Finding the seam
The refactor started with a question: what does the coordinator core actually need to know about a job?
Not what a work unit computes. Not what its parameters mean. Everything the pi coordinator did fell into two piles. One was generic machinery: hand out units, assign each to two nodes, compare result bytes, break ties with a third node, salt in canaries, score trust, eject liars, reassign work from dead sockets. The other was pi-specific: seeds, dart counts, the inside/total output, the times-four-at-the-end estimate.
The second pile became an interface. A workload, to the coordinator, is six capabilities:
export interface Workload {
readonly kernelId: string; // which WASM kernel nodes must run
readonly unitOps: bigint; // work per unit (the tax accounting unit)
exhausted(): boolean; // finite input space fully minted?
nextUnitParams(): UnitParams | null; // mint the next real unit
nextCanaryParams(): UnitParams; // mint the next known-answer unit
reference(params: UnitParams): UnitOutput; // trusted answer, for canaries
encode(output: UnitOutput): string; // canonical bytes for equality
validate(params: UnitParams, output: UnitOutput): boolean; // shape guard
newAggregate(): WorkloadAggregate; // fold verified outputs
}
Params and outputs are opaque to the core. It mints units through the generator, compares outputs through encode, checks canaries through reference, and folds results through the aggregate. It never looks inside. Adding a workload means registering one of these objects plus a kernel; the coordinator core is never edited.
Two entries deserve a closer look. encode exists because byte-equality verification needs a canonical byte representation; comparing JavaScript objects is how you end up debating whether {inside, total} equals {total, inside}. Every workload defines exactly one encoding (both mine use eight bytes, two little-endian u32s) and the core compares hex strings. And validate is not verification. It is a structural guard that keeps malformed wire JSON out of the byte-equality engine, so a node sending garbage gets penalized without ever reaching the comparison.
The second workload
A seam you have only threaded once is a hypothesis, not an architecture. So the test needed a workload that was genuinely different from pi, not pi with a different name.
I picked a distributed prime counter: count the primes in [0, N) exactly, using a segmented sieve of Eratosthenes. It differs from Monte Carlo pi on every axis that matters:
- Exact, not statistical. Pi converges; the prime count is a single correct integer. There is no "close enough."
- No randomness. Pi's determinism rests on a seeded PCG32 and a careful argument about IEEE-754 floats in WASM. The sieve is pure integer arithmetic, deterministic on every platform by construction. Nothing to argue.
- Finite input space. Pi's unit generator mints seeds 1, 2, 3, ... forever; you stop when you are satisfied. The prime job is over when the last range [k*span, (k+1)*span) is done. This is why
exhausted()exists on the interface at all: the first workload never needed it.
That last difference rippled into the canary design in a way I did not expect. Pi canaries are perfect: drawn from the same seed sequence as real units, wire-identical and statistically indistinguishable, and since the input space is infinite, spending seeds on them costs nothing. The prime job cannot do that. Canary outputs never enter the aggregate, and an exact, finite job cannot afford to lose real ranges to them, or the final count is wrong. So prime canaries come from a shadow region: same span, but starting at 10^10, far above any real job's limit. On the wire a shadow range looks exactly like a real one, and since every node sees disjoint ranges anyway, a single node cannot tell them apart. Colluding nodes comparing assignments could notice ranges outside the job's progression; that is an accepted limitation, documented rather than hidden.
One more constraint that only shows up when you care about verification: the coordinator needs a trusted reference implementation to precompute canary answers, and mine is TypeScript while the nodes run Rust compiled to WASM. For the two to be byte-identical, every integer in play has to be exact in a JavaScript f64. So the kernel enforces end <= 2^52, and inside that domain the TS sieve and the Rust sieve agree bit for bit, pinned by a golden vector: there are exactly 78,498 primes below one million, and both implementations had better say so.
The payoff
The prime workload ran through the coordinator core with zero modifications to it. Same assignment loop, same R=2 byte-equality, same tiebreak, same canary machinery, same trust scoring, same healing. The diff to the core for adding a whole second workload was empty.
And the verification story carried over intact. I ran the same experiment as last part, a node that returns plausible-but-wrong prime counts, and the unmodified machinery caught it: R=2 disagreement flagged the unit, the tiebreak resolved it, the trust score fell, the liar got ejected. The grid does not know it is counting primes. It knows two strangers disagreed about eight bytes, and that is enough.
That is the moment this stopped being a pi demo and became a small compute grid.
The honest boundary
"Workload-agnostic" is a statement about the coordinator, not a promise that any computation fits. It is worth being precise about the shape of work this grid can serve, because the shape is narrow.
A workload fits if it has all three of these properties:
- Embarrassingly parallel. Units must be independent. No unit may need another unit's output, because units run on strangers' machines in arbitrary order, get reassigned when tabs close, and are computed twice on purpose.
- Tiny I/O. A pi unit is described in a few bytes (seed, count) and answered in eight. A prime unit is the same (start, end in, primes and span out). The moment a unit needs a large input, say a slice of a dataset or a scene file, you are shipping data through home upload links to nodes that may vanish before finishing, and paying that shipping twice per unit because of R=2 verification.
- Deterministic, bit for bit. The entire trust model from part six is byte-equality between independent executions. Integer math qualifies trivially. Seeded PRNGs plus IEEE-754 f64 in WASM qualify with care. GPU floating point does not: WebGPU makes no bit-exactness promises across vendors, so the day this grid wants GPU workloads, the whole verification design has to be rethought, probably around tolerance bands or redundant-on-same-hardware schemes that are much weaker than byte-equality.
And some work will never fit, no matter how the coordinator evolves. Anything with real inter-node communication is out: browsers cannot accept inbound connections, so any node-to-node message is two WebSocket hops through the coordinator, and tightly coupled simulations (fluid dynamics, molecular dynamics, most scientific computing people first think of) exchange boundary data every step. Anything built on moving big data is out for the same I/O reasons, squared. Anything latency-sensitive is out, because a unit's true completion time includes the possibility that both its assignees close their laptops.
What is left is a real but narrow slice: parameter sweeps, brute-force search, Monte Carlo anything, integer and combinatorial problems that decompose into small self-describing ranges. Within that slice, the seam means a new workload is one registry entry, one kernel, and one worker dispatch arm, and it inherits verification, healing, and trust for free.
Whether that slice is worth anything as more than a proof of capability is the next and final question. First, though, the grid has to actually ship: a real deploy, a real domain, and real numbers from real strangers' browsers.
Next: Shipping it, and the honest scorecard. Previously: Trusting math from strangers.