<proto-sudoku>
A Stencil and Tailwind component in its own Shadow DOM: the board, the digit buttons, New Puzzle, Check ? and the status line. platform="cloudflare" points it at the relative path /api.
proto-sudoku
Classic sudoku on Cloudflare: static files serve the page, and a small Rust program compiled to WebAssembly generates every puzzle. One Worker, no database, no server-side state, no accounts.
worker — wrangler.jsonc
A Stencil and Tailwind component in its own Shadow DOM: the board, the digit buttons, New Puzzle, Check ? and the status line. platform="cloudflare" points it at the relative path /api.
Holds the 81 cells, the picked cell and its peers (row, column, box), and the legal digits for it. Decodes ref once, so Check compares every entry locally — no second request.
The puzzle, the picked cell, and the entries that passed a check. Unchecked entries stay in memory only, so a reload drops them.
The page loads the component from jsDelivr at an exact version (proto-sudoku-wc@0.1.340). Stencil lazy-loads hashed chunks through the same URL, so @latest could mix files from two releases; .wc-loader bumps the pin instead.
Any path that matches a file: the game page, /about, this page, /llms.txt, images and favicons. Most requests never run the Rust code.
Everything without a matching file. A small JS shim from worker-build loads the wasm module and hands it each request.
Runs worker-build --release, then the same Worker in a local workerd. No other services to start.
Game
One route does the work
Leftover demo — returns {"hello": name}
Static
Pages
Discovery — for LLMs & agents, not the app itself
Every Worker response — 404s and OPTIONS preflights included — gets permissive CORS headers in one place after routing (Access-Control-Allow-Origin: *, methods GET,OPTIONS), so any page can call the API. There is no key and no rate limit.
Handlers
fetch() — the router, then CORSpuzzle() — builds the JSONgreet() — the demo routewith_cors()Puzzle — plain Rust, no Worker types
new_puzzle()Sudoku::generate() — unique, minimal, symmetricsolution() — solved straight awaybase64 — encodes the answerBuild
worker-build --releaseopt-level = "s" — plus LTOgetrandom — js feature, crypto.getRandomValuesBench — benches/puzzle.rs
cargo bench — Criterion, nativeBecause new_puzzle() has no Worker types, the same function is benchmarked natively. The response carries no timings: Workers freezes the clock while code runs, so a timer would always read 0.
/api/puzzle — response
localStorage — proto-sudoku::
The solution ships with the puzzle on purpose: it's a casual game, and checking in the browser beats a round trip. Base64 hides the answer from a glance; it doesn't protect it.
Workers Builds had no Rust cache and took 6–7 minutes a push. The script builds locally, ships only a clean main that matches the remote, and tags the deploy with the commit.
cargo update, and only if Cargo.lock changed: check the wasm and native builds, then commit and push. Major-version bumps are reported, not applied. Deploys stay a separate, manual step.
sudoku.eswat2.app is a Workers Custom Domain, so Cloudflare manages its DNS and certificate. rust-sudoku-api.eswat2.workers.dev stays on too.
Containers could have run the old Actix binary unchanged, but need the paid plan. Before the port it lived on Heroku until the free tier ended in 2022. A serverless copy still runs on Vercel.