Chapter 17: web, the page at hooks.lmjtfy.fun

A plugin that lives in a terminal has a problem when you want to show it to someone: they would have to install it first. So jevhooks has one web page. It says what the plugin does, shows the four pictures of it doing it, makes the plugin's one promise in public, lists every hook point, draws the rules that decide, and points at this guide. That is all it does, and it is the whole of this folder.

The page is served by a Cloudflare Worker: a small program that Cloudflare runs on its own machines, near whoever asks, each time a request arrives. This one is written in Rust and compiled to WebAssembly. There is no server to keep running, no database, and nothing in the browser that remembers you.

Almost none of it is new code. The Jev sites (lmjtfy.fun, whiskers.lmjtfy.fun, and now this one) kept growing the same parts, so the parts were pulled out:

FromWhat the site gets
jev-ui (third-party/jevcrates)The look: stylesheet, fonts, the page's head, the nav, the windows with black title bars, the pink band, the link-preview tags, the preview card's frame.
jevhooks-events, jevhooks-rules (crates/, chapters 7 and 11)The plugin's own list of hook events, and its own rules: the page's map and its diagrams are read from the code the plugin runs.
rete-draw (third-party/rustcrates)A rules network as a picture.
Aldebaran (third-party/rustcrates, the aldebaran-* crates)Everything a site's Worker repeats: which address a request came to, the security headers, the request log, the responses, the embedded files.

What is left for this folder is about a thousand lines: the words, the pictures, a few worked cases, and which path gets which answer.

Aside: one copy of each picture. The four pictures are not copied here. They are the same files the chapters show (plugin/hooks/band.svg and friends), which mise run screenshots takes from a real session (chapter 16), and content.rs embeds them from where they are. Retake a picture and the guide and the site change together; they cannot show two different ones.

Aside: the card. Paste a link into Discord and it unfurls into a picture. That picture cannot be an SVG, because the apps that draw link previews do not draw SVG. So the site draws its card as an SVG in Rust (src/bin/card.rs, in the shared card look), and the build hands it to resvg to make the PNG the page's tags point at. It is made on every build and never committed.

Try it.

mise run test:web      # the page, the gate and the policy, natively, no Cloudflare
mise run web:dev       # http://localhost:8790/

Neither needs an account or a login. The tests build the whole page as a string and read it back: that every picture is there with words for someone who cannot see it, that the link preview has every tag Discord reads, that the page has no inline script.

For the people who maintain it

hooks.lmjtfy.fun is the canonical address. Any other address that is not local (the account's workers.dev one) answers GET and HEAD with a 308 to the same path and query there, so a link preview or an old link moves.

Nothing an agent runs here deploys. The owner deploys with mise run web:deploy (tools/deploy-web.sh), which needs CLOUDFLARE_API_TOKEN and CLOUDFLARE_ACCOUNT_ID from fnox or the environment, refuses with a message when either is missing, and passes SITE_ORIGIN (https://hooks.lmjtfy.fun).

Measured 2026-10-06 under wrangler dev: the page is 107 KB of HTML (ten drawings of the rules are about half of it), the four pictures 3.5 to 5.4 KB each, the card 7.7 KB.

In this folder

PathWhat
src/Chapter 18: the page and the Worker.
Cargo.tomlA workspace of its own, apart from the plugin's: this builds for wasm32 with the Workers runtime, and the plugin must stay buildable without the Workers toolchain.
Cargo.lockIts dependencies at the exact versions built.
wrangler.tomlThe Worker's name, its custom domain, the build command, the assets binding for the card, and the logging rule (no invocation logs).
.gitignoreWhat a build makes: target/, build/, public/preview/, .wrangler/.

← Previous: Chapter 16, tools/ · Up: jevhooks · Next: Chapter 18, web/src/ →